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?
(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.
the NRVO (Named Return Value Optimization)
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.
Compilers often perform Named Return Value Optimization (NRVO) in such cases, but it is not guaranteed.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With