alex alex - 5 months ago 38
C++ Question

rvalue reference in class template vs function template

#include <iostream>

template <typename T>
class test
test(T&& t)

template <typename T>
void succeed(T&& t)

int main()
int i = 1;
test<int> t(i); // failed to compile
succeed(i); // OK
return 0;

Error from GCC 5.2:
main.cpp: In function 'int main()':
main.cpp:20:18: error: cannot bind 'int' lvalue to 'int&&'
test t(i);
main.cpp:7:5: note: initializing argument 1 of 'test::test(T&&) [with T = int]'
test(T&& t)

Could someone explain why the class template cannot compile but function template is OK?


Your confusion is probably rooted in your assumption that in both cases T is int. This is why you presume that these two cases as similar. In reality they are not.

In the class version you are manually specifying what T is. You explicitly tell the compiler that T is int. Constructor parameter type T && in this case becomes int &&, which cannot bind to a regular lvalue. Hence the error.

In the function version you don't tell the compiler what T is, but instead you expect the compiler to deduce it. In situations like yours the language is deliberately designed to deduce T as int & (note: not as int, but rather as int &). Once T is deduced as int &, the so called "reference collapsing" rules lead to function parameter type T && becoming int & - an ordinary lvalue reference. This parameter can successfully bind to lvalue argument i.

That explains the difference you observe.

For the sake of experiment, in the latter case you can suppress template argument deduction and specify the template argument explicitly


That will forcefully specify T as int and lead to the very same error as in the class version for the very same reason.

Similarly, you can "simulate" function's behavior for your class by specifying the template argument as int &

test<int &> t(i);

The same "reference collapsing" rules will make your constructor invocation to compile successfully.