Derive the type of tuple elements in C ++ 11

I have the following code.

template <typename... Types> void print_tuple(const std::tuple<Types&&...>& value) { std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; } print_tuple(std::forward_as_tuple("test",1)); 

whose compiler complains about

 error: invalid initialization of reference of type 'const std::tuple<const char (&&)[5], int&&>&' from expression of type 'std::tuple<const char (&)[5], int&&>' print_tuple(std::forward_as_tuple("test",1)); 

why does the compiler infer the type of the first element in the tuple as const char (& &) [5]?

+6
source share
2 answers

Generally speaking, for successful deduction, an argument must have the same general appearance as a parameter. There are some exceptions from which T && can be deduced from U & (by choosing T = U & ), but this exception was not indicated.

14.8.2.5 Derivation of template arguments from the type [temp.deduct.type]

8 Template argument T , template TT template argument or non-type i template argument can be output if P and A have one of the following forms:

[...]

T&
T&&

[...]

This is not entirely clear, but this requires P (parameter) and A (argument) for both forms. They must be T&& or both T&& . Exceptions, circumstances in which T && can be deduced from U & , are performed by changing T && to plain T before the comparison is performed, in limited circumstances:

10 Similarly, if P has a form containing (T) , then each type of parameter P i corresponding list of parameter types P compared with the corresponding type of parameter A i corresponding list of parameters of type A If P and A are types of functions that have arisen from subtraction when accepting the address of the function template (14.8.2.2) or when deriving template arguments from the function declaration (14.8.2.6) and P i and A i are parameters of the top list of type parameters P and A , respectively, P i configured if it is an rvalue reference to the cv-unqualified template parameter, and A i is an lvalue, in this case the type of P i changes as the type of the template parameter (i.e., T&& changes simply to T ). [...]

and

14.8.2.1 Deriving template arguments from a function call [temp.deduct.call]

3 [...] If P is the rvalue reference to the cv-unqualified template parameter and the argument is lvalue, instead of A , the type is "lvalue reference to A " for the type. [...]

but a similar exception does not apply to your scenario.

This is the very principle that makes

 template <typename T> struct S { }; template <typename T> void f(S<const T>) { } int main() { f(S<void()>()); } 

invalid: const T cannot be inferred from void() , although T = void() will give exactly this result, and the call f<void()> will succeed.

Wintermute's deleted answer showed that you can use

 template <typename... Types> // vv-- change here void print_tuple(const std::tuple<Types...>& value) 

instead: it allows Types to be displayed as lvalue links, as rvalue links, or as non-links, depending on the type of value .

+2
source

Do you intend to use && in std::tuple<Types&&...> as a universal link? This is not a universal reference; this is an rvalue reference and can only be bound to r values. You can do this to check what the link is:

 template<typename T> class TD; 

Then define your template function:

 template <typename... Types> void print_tuple(const std::tuple<Types&&...>& value) { TD<decltype(value)> a; std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; } 

Then you will see a compilation error, for example:

 implicit instantiation of undefined template 'TD<const std::__1::tuple<std::__1::basic_string<char> &&, int &&> &>' 

You can see that even for the int type, it displays a link to rvalue. Rvalue links cannot link to lvalues. You can try this by calling:

 int i = 1; print_tuple(std::forward_as_tuple(i,1)); // error. 

Therefore, it is correct to output const char(&&)[5] , and the string literal cannot be converted to const char(&&)[5] . If you call print_tuple like:

 print_tuple(std::forward_as_tuple(string("test"),1)); 

It will work. Now type tuple<string&&, int&&> .

0
source

Source: https://habr.com/ru/post/984085/


All Articles