Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
489 views
in Technique[技术] by (71.8m points)

c++ - Why is RVO disallowed when returning a parameter?

It's stated in [C++11: 12.8/31] :

This elision of copy/move operations, called copy elision, is permitted [...] :

— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

This implies

#include <iostream>

using namespace std;

struct X
{
    X() { }
    X(const X& other) { cout << "X(const X& other)" << endl; }
};

X no_rvo(X x) {
    cout << "no_rvo" << endl;
    return x;
}

int main() {
    X x_orig;
    X x_copy = no_rvo(x_orig);

    return 0;
}

will print

X(const X& other)
no_rvo
X(const X& other)

Why is the second copy constructor required? Can't a compiler simply extend the lifetime of x?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Imagine no_rvo is defined in a different file than main so that when compiling main the compiler will only see the declaration

X no_rvo(X x);

and will have no idea whether the object of type X returned has any relation to the argument. From what it knows at that point, the implementation of no_rvo could as well be

X no_rvo(X x) { X other; return other; }

So when it e.g. compiles the line

X const& x = no_rvo(X());

it will do the following, when maximally optimizing.

  • Generate the temporary X to be passed to no_rvo as argument
  • call no_rvo, and bind its return value to x
  • destruct the temporary object it passed to no_rvo.

Now if the return value from no_rvo would be the same object as the object passed to it, then destruction of the temporary object would mean destruction of the returned object. But that would be wrong because the returned object is bound to a reference, therefore extending its lifetime beyond that statement. However simply not destructing the argument is also no solution because that would be wrong if the definition of no_rvo is the alternative implementation I've shown above. So if the function is allowed to reuse an argument as return value, there can arise situations where the compiler could not determine the correct behaviour.

Note that with common implementations, the compiler would not be able to optimize that away anyways, therefore it is not such a big loss that it is not formally allowed. Also note that the compiler is allowed to optimize the copy away anyway if it can prove that this doesn't lead to a change in observable behaviour (the so-called as-if rule).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...