Roman Maltsev
Roman Maltsev

Reputation: 395

Why passing an lvalue as an rvalue argument fails?

Passing lvalue reference as an rvalue reference argument does not compile. Compiler could create a temporary object with its copy constructor and pass it as an rvalue, but it does not. Still, it does call the constructor, if types do not match.

I'm interested, why does it works that way? Which design logic is here in the C++ standard, that forces the compiler to treat copy-constructor in a different way?

void do_smth(string && s)
{}

void f(const char * s)
{
  do_smth(s); // passes a temporary string constructed from const char*
}

void g(const string & s)
{
  do_smth(s); // does not compile
}

void h(const string & s)
{
  do_smth(string(s)); // ok again
}

What should I do if I do not want to implement the second signature do_smth(const string &)? Should I use pass-by-value void do_smth(string s) instead?

What are other differences betweeen by-value void do_smth(string s) and rvalue-reference void do_smth(string && s), given the object string has move constructor?

Upvotes: 3

Views: 600

Answers (2)

bruno
bruno

Reputation: 32586

in h you give a non const copy of the const string, so the const disappears contrarily to the g case

of course with void do_smth(string s){...} the copy is made at the call and both const and non const string can be given in argument

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385144

This is the whole point of rvalue references. They bind to rvalues, and not to lvalues.

That is how you ensure that the overload you wanted is invoked, when you have both an lvalue version and an rvalue version. For example, a copy constructor vs a move constructor.

It's a feature.

Also, your lvalue is const, which does not match the signature of do_smth even if it did not take an rvalue reference.

If you want do_smth to be able to take either kind of expression, either make it take a const string&, or make it a template and have it take a forwarding reference T&& (which looks like an rvalue reference but kind of isn't).

If you want do_smth to store its own version of the string then have it take a value: you can still avoid a copy if needs be by using std::move at the callsite (or by passing an rvalue, which will trigger the string's own move constructor).

See how all options are available and elegant, due to how the rvalue reference binding rules are made? It all fits together.

Deciding what to allow do_smth to take entirely depends on what "smth" it is going to "do", and only you can say what that is.

Upvotes: 4

Related Questions