为什么不考虑特定值的模板?
Why template is not taken under consideration for specific values?
阅读以下内容后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;
}
}
最后 "test" 使用 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.
std::integral_constant<T, v>
支持隐式转换为 T
,生成 v
.
由于您的 std::enable_if_t
,您的自定义 operator+
在模板参数替换期间失败,因为您的 std::enable_if_t
,但考虑到 LHS Position<std::numeric_limits<int>::max()>{}
和 RHS Offset<1>{}
都支持隐式转换对于内置类型,可以使用语言的内置 +
运算符。 LHS 转换为 int
,RHS 转换为 unsigned
,结果得到 int + unsigned
的类型,即 unsigned
.
阅读以下内容后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;
}
}
最后 "test" 使用 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.
std::integral_constant<T, v>
支持隐式转换为 T
,生成 v
.
由于您的 std::enable_if_t
,您的自定义 operator+
在模板参数替换期间失败,因为您的 std::enable_if_t
,但考虑到 LHS Position<std::numeric_limits<int>::max()>{}
和 RHS Offset<1>{}
都支持隐式转换对于内置类型,可以使用语言的内置 +
运算符。 LHS 转换为 int
,RHS 转换为 unsigned
,结果得到 int + unsigned
的类型,即 unsigned
.