RVO and NRVO optimisations + C++11 move operator

912 views Asked by At

I am trying to figure out how RVO and NRVO work along with the new C++11 move operators. I have drafted a dummy class with a few examples.

EDIT: Only most important part of the code is shown.

Full source code is available here.

I have two functions that get the class as reference and return either a value or a reference:

VOpt& fChangeClassRetRef(VOpt &m) {
    m.setX(21);
    return m;
}

VOpt fChangeClassRetValue(VOpt &m) {
    m.setX(21);
    return m;
}

When I call these functions I have as output the following:

VOpt &m14 = fChangeClassRetRef(m13);

m14 = fChangeClassRetRef(m11);
     -> Copy Assignment Operator

m14 = fChangeClassRetValue(m11);
     -> Copy Constructor
     -> C++11 Move Operator

When an lvalue reference is used no copy constructors are invoked. Otherwise, those functions (that receive a reference as parameter) still call copy constructors.

Is this feature compiler dependent? Am I doing something wrong?

2

There are 2 answers

0
Cassio Neri On

You have:

// Change the value of the class, return ref!
VOpt& fChangeClassRetRef(VOpt &m) {
    m.setX(21);
    return m; //#1
}

which was used this way:

// VOpt m11;
VOpt m12 = fChangeClassRetRef(m11); // #2

Let's analyse: #1 returns the name of an argument (not a local variable), so neither RVO nor NRVO applies here. Now fChangeClassRetRef returns a reference to wherever m is bound to, which, in #2 is m11. Therefore, we're initializing m12 with an lvalue reference to m11. In this case he compiler can't call the move constructor because it takes an rvalue and, as we have just seen, you are providing an lvalue. The copy constructor gets called.

The other case is similar. You have

VOpt fChangeClassRetValue(VOpt &m) {
    m.setX(21);
    return m;
}

which is called this way:

// VOpt m13; 
VOpt &m14 = fChangeClassRetRef(m13);

As above, there's no RVO neither NRVO here because you're returning the name of an argument.

The function returns by value and you're returning an lvalue. (In some circunstances compilers are allowed to treat the returning lvalue as if it was an rvalue but not in this case.) Hence the move constructor cannot be called and the copy constructor is invoked.

0
David Rodríguez - dribeas On
m14 = fChangeClassRetRef(m11);
     -> Copy Assignment Operator

The function returns a reference (lvalue), it cannot do move-assignment as the argument is not an rvalue.

m14 = fChangeClassRetValue(m11);
     -> Copy Constructor
     -> C++11 Move Operator

The copy constructor is triggered internally to create the returned value. It must be a copy constructor and not a move constructor as the argument is a reference (lvalue). The assignment of the returned value to m14 uses the move assignment operator as the argument is an rvalue.