Passing rvalue reference to const lvalue reference paremeter

Sven picture Sven · Jul 18, 2014 · Viewed 8.2k times · Source

I am trying to understand C++11 rvalue references and how to use them for optimal performance in my code.

Let's say we have a class A that has a member pointer to a large amount of dynamically allocated data.

Furthermore, a method foo(const A& a) that does something with an object of class A.

I want to prevent the copy constructor of A from being called when an object of A is passed to the function foo, since in that case it will perform a deep copy of the underlying heap data.

I tested passing an lvalue reference:

    A a;
    foo(a);

and passing an rvalue reference:

    foo(A());

In both cases the copy constructor was not called.

Is this expected or is this due to some optimization of my compiler (Apple LLVM 5.1)? Is there any specification about this?

Answer

Joseph Mansfield picture Joseph Mansfield · Jul 18, 2014

That is expected. If you pass an argument to a reference type parameter (whether lvalue or rvalue reference), the object will not be copied. That is the whole point of references.

The confusion you're having is pretty common. The choice of copy or move constructor only occurs when passing an object by value. For example:

void foo(A a);

When passing an A object to this function, the compiler will determine whether to use the copy or move constructor depending on whether the expression you pass is an lvalue or rvalue expression.

On the other hand, none of the following functions would even try to invoke the copy or move constructor because no object is being constructed:

void foo(A& a);
void foo(const A& a);
void foo(A&& a);
void foo(const A&& a);

It's important to note that you should rarely (if ever) have any reason to write a function, other than a move constructor/assignment operator, that takes an rvalue reference. You should be deciding between passing by value and passing by const lvalue reference:

  1. If you're going to need a copy of the object inside the function anyway (perhaps because you want to modify a copy or pass it to another function), take it by value (A). This way, if you're given an lvalue, it'll have to be copied (you can't avoid this), but if you're given an rvalue, it'll be optimally moved into your function.

  2. If you're not going to need a copy of the object, take it by const lvalue reference (const A&). This way, regardless of whether you're given an lvalue or rvalue, no copy will take place. You shouldn't use this when you do need to copy it though, because it prevents you from utilising move semantics.

From the sounds of it, you're not going to make any copies at all, so a const A& parameter would work.