简体   繁体   中英

deduce 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));

which 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 compiler deduce the type of the first element in the tuple to be const char (&&)[5]?

Generally speaking, for deduction to succeed, the argument needs to have the same general form as the parameter. There are some exceptions where T && can be deduced from U & (by selecting T = U & ), but no such exception was specified for this case.

14.8.2.5 Deducing template arguments from a type [temp.deduct.type]

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

[...]

T&
T&&

[...]

It's not exactly clear, but this requires P (the parameter) and A (the argument) to both have the same form. They need to both be of the T& form, or both of the T&& form. The exceptions, the circumstances where T && can be deduced from U & , are done by changing T && to plain T before the matching takes place, in limited circumstances:

10 Similarly, if P has a form that contains (T) , then each parameter type P i of the respective parameter-type-list of P is compared with the corresponding parameter type A i of the corresponding parameter-type-list of A . If P and A are function types that originated from deduction when taking the address of a function template (14.8.2.2) or when deducing template arguments from a function declaration (14.8.2.6) and P i and A i are parameters of the top-level parameter-type-list of P and A , respectively, P i is adjusted if it is an rvalue reference to a cv-unqualified template parameter and A i is an lvalue reference, in which case the type of P i is changed to be the template parameter type (ie, T&& is changed to simply T ). [...]

and

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

3 [...] If P is an rvalue reference to a cv-unqualified template parameter and the argument is an lvalue, the type "lvalue reference to A " is used in place of A for type deduction. [...]

but no similar exception applies to your scenario.

It's this same principle that renders

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

invalid: const T cannot be deduced from void() , even though T = void() would give exactly that result, and calling f<void()> would 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: this allows Types to be deduced as lvalue references, as rvalue references, or as non-references, depending on the type of value .

Did you intend to use && in std::tuple<Types&&...> as a universal reference? This is not universal reference; it is rvalue reference and can only bind to rvalues. You can do like this to check what kind of reference it 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 the compilation error like:

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 deduces to be rvalue reference. Rvalue references cannot bind to lvalues. You can try that by calling:

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

Therefore, it is correct that const char(&&)[5] is deduced, and a 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 the type is tuple<string&&, int&&> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM