Constexpr template argument

GCC (5.3) and Clang (3.8) state that the first line in test bad and the second is ok. MSVC (2015.2) says that both values ​​are invalid.

 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 } 

What, exactly, according to the standard, is there a difference? Is this code legal?


clang error for first line:

 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 error:

 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 ...>' 
+6
source share
1 answer

tl; dr I think that the behavior of gcc and clang is correct in both cases.


There is a subtle difference between your two challenges. Let me just add an alias to illustrate:

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

When you write:

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

Zero() is a type. This is a null function that returns Zero . Thus, you call the version of std::get , which takes the type: std::get<T>() . Since there is no element of type Zero() in tuple , this is an error.

On the other hand, when you write:

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

n not a type - it is only a value. Since std::integral_constant has a constexpr operator size_t() , it is used, and you call std::get<I>() , which does what you expect.

The same could be done simply by using parenthesis initialization:

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

since Zero{} definitely not a type.

+6
source

All Articles