Let's take the following method as an example:
void Asset::Load( const std::string& path )
{
// complicated method....
}
General use of this method would be as follows:
Asset exampleAsset;
exampleAsset.Load("image0.png");
Since we know most of the time the Path is a temporary rvalue, does it make sense to add an Rvalue version of this method? And if so, is this a correct implementation;
void Asset::Load( const std::string& path )
{
// complicated method....
}
void Asset::Load( std::string&& path )
{
Load(path); // call the above method
}
Is this a correct approach to writing rvalue versions of methods?
Best Answer
For your particular case, the second overload is useless.
With the original code, which has just one overload for
Load
, this function is called for lvalues and rvalues.With the new code, the first overload is called for lvalues and the second is called for rvalues. However, the second overload calls the first one. At the end, the effect of calling one or the other implies that the same operation (whatever the first overload does) will be performed.
Therefore, the effects of the original code and the new code are the same but the first code is just simpler.
Deciding whether a function must take an argument by value, lvalue reference or rvalue reference depends very much on what it does. You should provide an overload taking rvalue references when you want to move the passed argument. There are several good references on move semantincs out there, so I won't cover it here.
Bonus:
To help me make my point consider this simple
probe
class:Now consider this function:
Calling
f("foo");
produces the following output:No surprises here: we create a temporary
probe
passing theconst char*
"foo"
. Hence the first output line. Then, this temporary is bound top
and a copyq
ofp
is created insidef
. Hence the second output line.Now, consider taking
p
by value, that is, changef
to:The output of
f("foo");
is nowSome will be surprised that in this case: there's no copy! In general, if you take an argument by reference and copy it inside your function, then it's better to take the argument by value. In this case, instead of creating a temporary and copying it, the compiler can construct the argument (
p
in this case) direct from the input ("foo"
). For more information, see Want Speed? Pass by Value. by Dave Abrahams.There are two notable exceptions to this guideline: constructors and assignment operators.
Consider this class:
The constructor takes a
probe
by const reference and then copy it top
. In this case, following the guideline above doesn't bring any performance improvement andprobe
's copy constructor will be called anyway. However, takingq
by value might create an overload resolution issue similar to the one with assignment operator that I shall cover now.Suppose that our class
probe
has a non-throwingswap
method. Then the suggested implementation of its assignment operator (thinking in C++03 terms for the time being) isThen, according to the guideline above, it's better to write it like this
Now enter C++11 with rvalue references and move semantics. You decided to add a move assignment operator:
Now calling the assignment operator on a temporary creates an ambiguity because both overloads are viable and none is preferred over the other. To resolve this issue, use the original implementation of the assignment operator (taking the argument by const reference).
Actually, this issue is not particular to constructors and assignment operators and might happen with any function. (It's more likely that you will experience it with constructors and assignment operators though.) For instance, calling
g("foo");
wheng
has the following two overloads raises the ambiguity: