ConstexPR模板参数怪异

constexpr template argument weirdness

本文关键字:参数 ConstexPR      更新时间:2023-10-16

GCC(5.3(和Clang(3.8(声称test的第一行不好,但第二行很好。MSVC (2015.2( 说,两者都无效。

template< typename N, typename T >
void f( N n, T t ) { std::get< n >( t ); }
void test() {
    std::get< std::integral_constant< size_t, 0 >() >( std::make_tuple( 123 ) ); // not ok
    f( std::integral_constant< size_t, 0 >(), std::make_tuple( 123 ) ); // ok for gcc, clang, but not msvc
}

根据标准,究竟有什么区别?这个代码一开始合法吗?

<小时>

第一行的叮当错误:

In file included from main.cpp:2:
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/tuple:874:34: error: no matching function for call to '__get_helper2'
    { return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
                                 ^~~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:10: note: in instantiation of function template specialization 'std::get<std::integral_constant<unsigned long, 0> (), int>' requested here
    std::get<std::integral_constant<size_t, 0>()>( std::make_tuple( 123 ) ); // not ok
         ^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/tuple:856:5: note: candidate template ignored: could not match '_Tuple_impl' against 'tuple'
    __get_helper2(_Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
    ^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/tuple:861:5: note: candidate template ignored: could not match '_Tuple_impl' against 'tuple'
    __get_helper2(const _Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
    ^
1 error generated.

GCC 错误:

In file included from main.cpp:2:0:
/usr/local/include/c++/5.3.0/tuple: In instantiation of 'constexpr _Tp&& std::get(std::tuple<_Elements ...>&&) [with _Tp = std::integral_constant<long unsigned int, 0ul>(); _Types = {int}]':
main.cpp:10:75:   required from here
/usr/local/include/c++/5.3.0/tuple:874:57: error: no matching function for call to '__get_helper2(std::tuple<int>&)'
     { return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
                                                         ^
/usr/local/include/c++/5.3.0/tuple:856:5: note: candidate: template<class _Head, long unsigned int __i, class ... _Tail> constexpr _Head& std::__get_helper2(std::_Tuple_impl<__i, _Head, _Tail ...>&)
     __get_helper2(_Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
     ^
/usr/local/include/c++/5.3.0/tuple:856:5: note:   template argument deduction/substitution failed:
/usr/local/include/c++/5.3.0/tuple:874:57: note:   mismatched types 'std::integral_constant<long unsigned int, 0ul>()' and 'int'
     { return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
                                                         ^
/usr/local/include/c++/5.3.0/tuple:874:57: note:   'std::tuple<int>' is not derived from 'std::_Tuple_impl<__i, std::integral_constant<long unsigned int, 0ul>(), _Tail ...>'
/usr/local/include/c++/5.3.0/tuple:861:5: note: candidate: template<class _Head, long unsigned int __i, class ... _Tail> constexpr const _Head& std::__get_helper2(const std::_Tuple_impl<__i, _Head, _Tail ...>&)
     __get_helper2(const _Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
     ^
/usr/local/include/c++/5.3.0/tuple:861:5: note:   template argument deduction/substitution failed:
/usr/local/include/c++/5.3.0/tuple:874:57: note:   mismatched types 'std::integral_constant<long unsigned int, 0ul>()' and 'int'
     { return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
                                                         ^
/usr/local/include/c++/5.3.0/tuple:874:57: note:   'std::tuple<int>' is not derived from 'const std::_Tuple_impl<__i, std::integral_constant<long unsigned int, 0ul>(), _Tail ...>'

tl;dr 我认为 gcc 和 clang 在这两种情况下的行为都是正确的。

<小时 />

您的两个电话之间存在细微差别。让我添加一个别名来说明:

using Zero = std::integral_constant<size_t, 0>;
auto tuple = std::make_tuple(123);

当你写:

std::get<Zero()>(tuple);

Zero()是一种类型。它是一个返回Zero的空函数。因此,您调用的 std::get 版本采用类型:std::get<T>() 。由于tuple中没有类型为 Zero() 的元素,这是一个错误。

另一方面,当你写:

Zero n;
std::get<n>(tuple);

n不是一个类型 - 它只是一个值。由于std::integral_constant有一个constexpr operator size_t(),那个被习惯了,你最终会调用std::get<I>(),它做了你所期望的。

也可以通过简单地使用大括号初始化来实现相同的操作:

std::get<Zero{}>(tuple);

因为Zero{}绝对不是一种类型。