简体   繁体   中英

Return value optimization of a member of a local stack variable

I have a piece of generic code that, when instantiated, boils down to:

struct A{...};

A f1(){
    A ret;
    std::pair<A&, int> p(ret, 1);
    p = g(); // g returns a pair<A, int>, but I am not interested in g
    return ret; // RVO :)
};

As far as I understand, this will work with RVO.

The question is, would this other code return the object of type A with RVO?

A f2(){
    std::pair<A, int> p;
    p = g();
    return p.first; // RVO ?
};

I understand that since the return object is obscured, it will not do RVO or that the compiler may not be able to pick the "optimization". But I don't see a fundamental reason why it wouldn't be possible, in other words, I think for consistency, it should do RVO (which I want).

The reason I ask, it because I think f2 is more elegant code than f1 .

Should this last version do RVO? If so, is it compiler dependent?


My crude test with gcc 8.1 gives that RVO doesn't work in f2 and f3 below:

struct A{
    double something;
    A() = default;
    A(A const& other) : something(other.something){std::cerr << "cc" << '\n';}
    A(A&& other) : something(other.something){std::cerr << "mc" << '\n';}
};

A f1(){
    A ret;
    std::pair<A&, int> p(ret, 1);
    p.first.something = 5.;
    return ret; // RVO :)
};

A f2(){
    std::pair<A, int> p;//(ret, 1);
    p.first.something = 5.;
    return p.first; // no RVO :(
};

A f3(){
    std::pair<A, int> p;//(ret, 1);
    p.first.something = 5.;
    return std::move(p).first; // no RVO :(
};


int main(){
    A a1 = f1(); // prints nothing, RVO
    A a2 = f2(); // prints "cc"; no RVO! Why?
    A a3 = f3(); // prints "mc" (no much gain in this case); still no RVO!
}

For RVO to work, the return value needs to be instantiated in the storage where the caller expects to find it. Perhaps this is a register whose name is specified by the calling convention, or perhaps it is on the stack.

std::pair<A, int> is essentially:

struct foo {
    A first;
    int second;
};

Now, if the return value needs to be stored at a specific place with sizeof(A) , but the pair has a larger size, the pair cannot possibly be stored there. The only way RVO could still be made to work is if the callee knew that the sizeof(int) bytes following the return value were allowed to be clobbered while the function was executing. But the language probably shouldn't require RVO in such a case, because it might not be implementable in every calling convention.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM