The following C++11 code does not compile:
struct T {};
void f(T&&) { }
void g(T&& t) { f(t); }
int main()
{
g(T());
}
The correct way to do this is:
void g(T&& t) { f(move(t)); }
This is very difficult to explain in the correct natural language terminology. The parameter t
seems to lose its "&&" status which it needs to have reinstated with the std::move
.
What do you call the T()
in g(T())
?
What do you call the T&&
in g(T&& t)
?
What do you call the t
in g(T&& t)
?
What do you call the t
in f(t)
and f(move(t))
?
What do you call the return value of move(t)
?
What do you call the overall effect?
Which section(s) of the standard deal with this issue?
The key point is that a parameter T&& b
can only bind to an rvalue, but when referred to later the expression b
is an lvalue.
So the argument to the function must be an rvalue, but inside the function body the parameter is an lvalue because by then you've bound a reference to it and given it a name and it's no longer an unnamed temporary.
An expression has a type (e.g. int
, string
etc.) and it has a value category (e.g. lvalue or rvalue) and these two things are distinct.
A named variable which is declared as T&& b
has type "rvalue reference to T
" and can only be bound to an rvalue, but when you later use that reference the expression b
has value category "lvalue", because it has a name and refers to some object (whatever the reference is bound to, even though that was an rvalue.) This means to pass b
to another function which takes an rvalue you can't just say f(b)
because b
is an lvalue, so you must convert it (back) to an rvalue, via std::move(b)
.
All parameters are lvalues, even if their type is "rvalue reference". They have names, so you can refer to them as often as you want. If named rvalue references were rvalues, you would get surprising behavior. We do not want implicit moves from lvalues, that's why you have to explicitly write std::move
.
What do you call the T() in g(T()) ?
A temporary (which is movable).
What do you call the T&& in g(T&& t) ?
T&& is an r-value reference and represent an object that can be moved.
What do you call the t in g(T&& t) ?
t is actually an l-value since you can refer to it by name.
What do you call the t in f(t) and f(move(t)) ?
What do you call the return value of move(t)?
An r-value reference
As a note; you should maybe call the struct C, and write a separate example where T is actually templetized. The code needs to be different then, because in a function template< typename T > void f( T&& t );
you cannot simply use std::move()
without being very careful, since T can actually be a const&
, in which case you must not use std::move()
but instead use perfect forwarding with std::forward< T >( t )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With