【问题标题】:Why template is not taken under consideration for specific values?为什么不考虑特定值的模板?
【发布时间】:2016-12-07 22:37:23
【问题描述】:

在阅读了以下blog entry at insooth.github.io之后,我将代码重写为以下形式,结果令我惊讶:

#include <iostream>
#include <limits>
#include <type_traits>

template <unsigned d>
using Offset = std::integral_constant<unsigned, d>;

template <int p>
struct Position : std::integral_constant<int, p> {
  static constexpr auto max = std::numeric_limits<int>::max();

  template <unsigned i>
  constexpr auto operator+(Offset<i>)
      // assertion is equivalent to: value + i <= max
      -> std::enable_if_t<(i <= Position::max - Position::value), Position<Position::value + i>> {
    return Position<Position::value + i>{};
  }
};

int main() {
  {
    auto p = Position<11>{} + 1;
    static_assert(std::is_same<decltype(p), int>::value, "");
  }

  {
    auto p = Position<std::numeric_limits<int>::max()>{};
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, "");
  }

  // this will fail
  // auto poverflow = Position<std::numeric_limits<int>::max() + 1>{};

  {
    auto p = Position<std::numeric_limits<int>::max()>{} + Offset<0>{}; // OK
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, "");
    std::cout << p << std::endl;
  }

  {
    //
    // MARKED
    //
    auto p = Position<std::numeric_limits<int>::max()>{} + Offset<1>{}; // OK but shouldn't
    static_assert(std::is_same<decltype(p), unsigned int>::value, "");
    std::cout << p << std::endl;
  }

  {
    // compiles ok with clang but fails with gcc
    auto p = Position<std::numeric_limits<int>::min()>{} +
      Offset<std::numeric_limits<unsigned>::max()>{}; // OK but wrong type
    static_assert(std::is_same<decltype(p), unsigned int>::value, "");
    std::cout << p << std::endl;
  }
}

最后一次“测试”在 clang 3.9 下编译成功,但在 gcc 6.2 下编译失败。

现场演示:http://coliru.stacked-crooked.com/a/61a5bf3040afaadb

有人可以解释为什么标记线

static_assert(std::is_same<decltype(p), unsigned int>::value, "");

编译 -> 为什么p 的类型是unsigned int

编辑

按照@hvd 的建议进行小改动后,gcc 编译(严重)并被 clang 拒绝。

代码:

#include <iostream>
#include <limits>
#include <type_traits>

template <unsigned d>
struct Offset
{
  static constexpr unsigned value = d;
};

template <int p>
struct Position : std::integral_constant<int, p>
{
  static constexpr auto max = std::numeric_limits<int>::max();

  template <unsigned i>
  constexpr auto operator+(Offset<i>)
      // assertion is equivalent to: value + i <= max
      -> std::enable_if_t<(i <= Position::max - Position::value), Position<Position::value + i>>
  {
    return Position<Position::value + i>{};
  }
};

int main()
{
  {
    auto p = Position<11>{} + 1;
    static_assert(std::is_same<decltype(p), int>::value, "");
  }

  {
    auto p = Position<11>{} + Offset<1>{};
    static_assert(std::is_same<decltype(p), Position<12>>::value, "");
  }

  {
    auto p = Position<std::numeric_limits<int>::max()>{};
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, "");
  }

  auto poverflow = Position<std::numeric_limits<int>::max() + 0>{};
  // this would fail
  // auto poverflow = Position<std::numeric_limits<int>::max() + 1>{};

  {
    auto p = Position<std::numeric_limits<int>::max()>{} + Offset<0>{}; // OK now
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, "");
    static_assert(!std::is_same<decltype(p), unsigned int>::value, "");
    std::cout << p << std::endl;
  }

  {
    auto p = Position<std::numeric_limits<int>::min()>{} + Offset<0>{}; // OK but wrong type
    static_assert(!std::is_same<decltype(p), unsigned int>::value, "");
    std::cout << p << std::endl;
  }
}

输出:

g++


2147483647
-2147483648


clang


In file included from main.cpp:1:
In file included from /usr/include/c++/v1/iostream:38:
In file included from /usr/include/c++/v1/ios:216:
In file included from /usr/include/c++/v1/__locale:15:
/usr/include/c++/v1/string:1938:44: error: 'basic_string<_CharT, _Traits, _Allocator>' is missing exception specification 'noexcept(is_nothrow_copy_constructible<allocator_type>::value)'
basic_string<_CharT, _Traits, _Allocator>::basic_string(const allocator_type& __a)
                                           ^
/usr/include/c++/v1/string:1326:40: note: previous declaration is here
    _LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a)
                                       ^
main.cpp:57:58: error: invalid operands to binary expression ('Position<std::numeric_limits<int>::min()>' and 'Offset<0>')
    auto p = Position<std::numeric_limits<int>::min()>{} + Offset<0>{}; // OK but wrong type
             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~
main.cpp:20:18: note: candidate template ignored: substitution failure [with i = 0]: non-type template argument evaluates to 2147483648, which cannot be narrowed to type 'int'
  constexpr auto operator+(Offset<i>)
                 ^
/usr/include/c++/v1/iterator:640:1: note: candidate template ignored: could not match 'reverse_iterator' against 'Offset'
operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x)
^
/usr/include/c++/v1/iterator:1044:1: note: candidate template ignored: could not match 'move_iterator' against 'Offset'
operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x)
^
/usr/include/c++/v1/iterator:1400:1: note: candidate template ignored: could not match '__wrap_iter' against 'Offset'
operator+(typename __wrap_iter<_Iter>::difference_type __n,
^
/usr/include/c++/v1/string:3946:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
^
/usr/include/c++/v1/string:3959:1: note: candidate template ignored: could not match 'const _CharT *' against 'Position<std::numeric_limits<int>::min()>'
operator+(const _CharT* __lhs , const basic_string<_CharT,_Traits,_Allocator>& __rhs)
^
/usr/include/c++/v1/string:3971:1: note: candidate template ignored: could not match 'basic_string' against 'Offset'
operator+(_CharT __lhs, const basic_string<_CharT,_Traits,_Allocator>& __rhs)
^
/usr/include/c++/v1/string:3982:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs)
^
/usr/include/c++/v1/string:3994:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, _CharT __rhs)
^
/usr/include/c++/v1/string:4008:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, const basic_string<_CharT, _Traits, _Allocator>& __rhs)
^
/usr/include/c++/v1/string:4016:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs)
^
/usr/include/c++/v1/string:4024:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs)
^
/usr/include/c++/v1/string:4032:1: note: candidate template ignored: could not match 'const _CharT *' against 'Position<std::numeric_limits<int>::min()>'
operator+(const _CharT* __lhs , basic_string<_CharT,_Traits,_Allocator>&& __rhs)
^
/usr/include/c++/v1/string:4040:1: note: candidate template ignored: could not match 'basic_string' against 'Offset'
operator+(_CharT __lhs, basic_string<_CharT,_Traits,_Allocator>&& __rhs)
^
/usr/include/c++/v1/string:4049:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, const _CharT* __rhs)
^
/usr/include/c++/v1/string:4057:1: note: candidate template ignored: could not match 'basic_string' against 'Position'
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, _CharT __rhs)
^
2 errors generated.

【问题讨论】:

  • @skypjack 代码中有一条关于它的注释。请检查一下
  • 是的,我的意思是哪个版本。 ;-)

标签: c++ templates c++14 template-meta-programming typetraits


【解决方案1】:

std::integral_constant&lt;T, v&gt; 支持隐式转换为T,生成v

由于您的std::enable_if_t,您的自定义operator+ 在模板参数替换过程中失败,但鉴于LHS Position&lt;std::numeric_limits&lt;int&gt;::max()&gt;{} 和RHS Offset&lt;1&gt;{} 都支持隐式转换为内置类型,该语言的内置可以使用+ 运算符。 LHS转换为int,RHS转换为unsigned,结果得到int + unsigned的类型,即unsigned

【讨论】:

  • 我们能否以某种方式阻止这种隐式转换?
  • @Patryk:当然——不要从std::integral_constant 派生Offset&lt;N&gt;
  • @Patryk 我认为这应该是一个单独的问题,但Position::value + i 是有问题的,因为int + unsigned 也是如此,意思是unsigned。您的Position::value 是负数且i == 0,因此结果将超出int 的范围,从而使转换回int 是不允许在{} 内进行的缩小转换。如果您想支持大于INT_MAX 的偏移量,则要正确处理可能会很棘手。我怀疑您必须将偏移值转换为int,如果它超出int 的范围,请将其拆分为多个较小的添加。
  • @hvd 好吧,在这里处理所有可能的情况并不容易:/ 我认为您已经回答了我的问题,如果需要,我可以创建一个新问题。谢谢
猜你喜欢
  • 2018-05-14
  • 1970-01-01
  • 2012-06-04
  • 2013-10-01
  • 2017-04-26
  • 2021-10-12
  • 2013-04-25
  • 2013-05-17
  • 1970-01-01
相关资源
最近更新 更多