Let T
be an arbitrary type. Consider a function that takes a const
[lvalue] reference:
void f(const T &obj);
Suppose that this function internally makes a call to another function, which has an rvalue reference overload:
void g(T &&obj);
If we pass an rvalue to f
, will the rvalue reference overload of g
be called, or will it fail to do so since it has been "converted"/bound to a const
lvalue reference?
Similarly, if f
called a function that takes an instance of T
by value,
void h(T obj);
and T
has a move constructor, (i.e. T(T &&);
), will the move constructor be called, or will the copy constructor be called?
In conclusion, if we wanted to ensure that, when calling f
on an rvalue, the rvalue reference is passed around keeping its rvalue "status", would we necessarily have to provide an rvalue reference overload for f
?
When you use the name of a reference as an expression, that expression is always an lvalue. No matter if it's a lvalue reference or an rvalue reference. It also doesn't matter if the reference was initialized with an lvalue or an rvalue.
So in
void f(const T &obj) {...}
,obj
is always an lvalue, regardless of what you pass as an argument.Also note that value category is determined at compile time. Since
f
is not a template, value category of every expression in it can't depend on arguments you pass.Thus:
No.
No.
Providing an overload is one option. Note that in this case you have to explicitly call
std::move
in the rvalue overload.Another option is using forwarding references, as Nicol Bolas suggests:
Here,
std::forward
essentially acts as a 'conditionalmove
'. It movest
if an rvalue was passed to it, and does nothing otherwise.