Move or Named Return Value Optimization (NRVO)?

C++OptimizationC++11Move SemanticsReturn Value-Optimization

C++ Problem Overview


Lets say we have the following code:

std::vector<int> f()
{
  std::vector<int> y;
  ...
  return y;
} 

std::vector<int> x = ...
x = f();

It seems the compiler has two approaches here:

(a) NRVO: Destruct x, then construct f() in place of x.
(b) Move: Construct f() in temp space, move f() into x, destruct f().

Is the compiler free to use either approach, according to the standard?

C++ Solutions


Solution 1 - C++

The compiler may NRVO into a temp space, or move construct into a temp space. From there it will move assign x.

Update:

Any time you're tempted to optimize with rvalue references, and you're not positive of the results, create yourself an example class that keeps track of its state:

  • constructed
  • default constructed
  • moved from
  • destructed

And run that class through your test. For example:

#include <iostream>
#include <cassert>

class A
{
    int state_;
public:
    enum {destructed = -2, moved_from, default_constructed};

    A() : state_(default_constructed) {}
    A(const A& a) : state_(a.state_) {}
    A& operator=(const A& a) {state_ = a.state_; return *this;}
    A(A&& a) : state_(a.state_) {a.state_ = moved_from;}
    A& operator=(A&& a)
        {state_ = a.state_; a.state_ = moved_from; return *this;}
    ~A() {state_ = destructed;}

    explicit A(int s) : state_(s) {assert(state_ > default_constructed);}

    friend
    std::ostream&
    operator<<(std::ostream& os, const A& a)
    {
        switch (a.state_)
        {
        case A::destructed:
            os << "A is destructed\n";
            break;
        case A::moved_from:
            os << "A is moved from\n";
            break;
        case A::default_constructed:
            os << "A is default constructed\n";
            break;
        default:
            os << "A = " << a.state_ << '\n';
            break;
        }
        return os;
    }

    friend bool operator==(const A& x, const A& y)
        {return x.state_ == y.state_;}
    friend bool operator<(const A& x, const A& y)
        {return x.state_ < y.state_;}
};

A&& f()
{
    A y;
    return std::move(y);
}

int main()
{
    A a = f();
    std::cout << a;
}

If it helps, put print statements in the special members that you're interested in (e.g. copy constructor, move constructor, etc.).

Btw, if this segfaults on you, don't worry. It segfaults for me too. Thus this particular design (returning an rvalue reference to a local variable) is not a good design. On your system, instead of segfaulting, it may print out "A is destructed". This would be another sign that you don't want to do this.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionClintonView Question on Stackoverflow
Solution 1 - C++Howard HinnantView Answer on Stackoverflow