Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Move or Named Return Value Optimization (NRVO)?

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?

like image 949
Clinton Avatar asked Jun 04 '11 00:06

Clinton


People also ask

What is named return value optimization?

(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.

What is NRVO?

the NRVO (Named Return Value Optimization)

How does return value optimization work?

In the context of the C++ programming language, return value optimization (RVO) is a compiler optimization that involves eliminating the temporary object created to hold a function's return value. RVO is allowed to change the observable behaviour of the resulting program by the C++ standard.

Is NRVO guaranteed?

Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.


1 Answers

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.

like image 61
Howard Hinnant Avatar answered Sep 18 '22 16:09

Howard Hinnant