Forth to the Past: passing T&& as a parameter

This is a continuation of the series about C++ rvalue references. First post, second post, third post. Rvalue references are unique, because if you have a variable of type T&&, you cannot pass it to a function that accepts a T&&.  

class SomeClass;
void f(SomeClass&& ref);
void g(SomeClass&& ref)
{
f(ref); // error C2664: cannot convert argument 1 from 'SomeClass' to 'SomeClass &&'
}

Of course, there is a good explanation for this, but it still feels like a violation of fundamental violation of software development. If you have a function that accepts bananas, and you have a banana, you should be able to pass your banana to that function, shouldn’t you? Of course, even prior to rvalue references you could stop this from happening, e.g. by  making copy constructor private. Consider:

class SomeClass { SomeClass(SomeClass const&); /* private copy constructor */ };
void f(SomeClass c);
void g(SomeClass c)
{
f(c); // error C2248: cannot access private member declared in class 'SomeClass'
}

But that’s different, since in that case things work as expected by default, and you need to make an effort to break them. With rvalues things are weird by default.

The reason for that is that rvalue is by definition something without a name. Once we create a parameter out of it and give it a name (“ref” in our example), it stops being an rvalue. “ref” binds to an rvalue, but is in itself an lvalue. We can say ref=42; and it would be legal. Rvalue-ness is not transitive. To convert “ref” back to rvalue, we need to use either move() or forward(). E.g. this works:

class SomeClass;
void f(SomeClass&& ref);
void g(SomeClass&& ref)
{
f(std::move(ref));
}

See also: what is the difference between move() and forward()?

Posted in

Leave a Reply

Your email address will not be published. Required fields are marked *