问题
#include <iostream>
template <typename T>
class test
{
public:
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? Thanks.
回答1:
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
succeed<int>(i);
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.
回答2:
In succeed
, T&& t
is a forwarding reference, not an rvalue reference. But in test
, it is an rvalue reference.
A forwarding reference happens only when the parameter is T&&
, and T
is a template parameter of that function. In your code T
is a template parameter of the enclosing class, so it doesn't count as a forwarding reference.
A forwarding reference may bind to both lvalues and rvalues.
During the drafting of C++11 it was suggested to use different syntax for forwarding references than rvalue references (instead of using T&& t
for both); however the committee eventually settled on the current behaviour.
For a more detailed description of template parameter deduction, including a more precise specification of when T&&
becomes a forwarding reference, see here -- search for the term "forwarding reference" to find the special rules for forwarding references.
来源:https://stackoverflow.com/questions/39785755/rvalue-reference-in-class-template-vs-function-template