Does RVO work on object members?

南楼画角 提交于 2019-12-18 07:35:09

问题


Consider the following:

struct A { /* ... */ };

A foo() {
  auto p = std::make_pair(A{}, 2);
  // ... do something
  return p.first;
}

auto a = foo();

Will p.first be copied, moved or RVO-ed?


回答1:


I've found in Visual Studio 2010 and in gcc-5.1 RVO is not applied (see for example http://coliru.stacked-crooked.com/a/17666dd9e532da76).

The relevant section of the standard is 12.8.31.1 [class.copy]. It states that copy elision is permitted (my highlighting):

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 parameter or a variable introduced by the exception-declaration of a handler ([except.handle])) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value

Since p.first is not the name of an object, RVO is prohibited.




回答2:


Just to add a little more fuel, how would this function if RVO were in play? The caller has put an instance of A somewhere in memory and then calls foo to assign to it (even better, let's assume that that A was a part of a larger struct, and let's assume that it is correctly aligned such that the next member of the struct is immediately after that instance of A). Assuming RVO were in play, the first portion of p is located where the caller wanted it, but where does the int that is second get placed? It has to go right after the instance of A in order to keep the pair functioning correctly, but at the source location, there's some other member right after that instance of A.

I would expect that RVO would not be happening in this place as you are only returning a portion of a larger object. A move could happen as first would have to be left in a destructible state.




回答3:


@atkins got here first with the answer. Just adding this little test program which you may find useful in future when tracking move/assign behaviour.

#include <iostream>
#include <string>

using namespace std::string_literals;

struct A {
    A()
    : history("created")
    {
    }

    A(A&& r)
    : history("move-constructed,"s + r.history)
    {
        r.history = "zombie: was "s + r.history;
    }
    A(const A& r)
    : history("copied from: " + r.history)
    {
    }
    ~A() {
        history = "destroyed,"s + history;
        std::cout << history << std::endl;
    }
    A& operator=(A&& r) {
        history = "move-assigned from " + r.history + " (was "s + history + ")"s;
        r.history = "zombie: was "s + r.history;
        return *this;
    }
    A& operator=(const A&r ) {
        history = "copied from " + r.history;
        return *this;
    }
    std::string history;
};

A foo() {
    auto p = std::make_pair(A{}, 2);
    // ... do something
    return p.first;
}



auto main() -> int
{
    auto a = foo();
    return 0;
}

example output:

destroyed,zombie: was created
destroyed,move-constructed,created
destroyed,copied from: move-constructed,created



回答4:


Consider following code:

struct A {};
struct B {};
struct C { B c[100000]; };

A callee()
{
    struct S
    {
        A a;
        C c;
    } s;
    return s.a;
}

void caller()
{
    A a = callee();
    // here should lie free unused spacer of size B[100000]
    B b;
}

"Partial" RVO should result in excessive stack usage bloating in caller, because (I think) S can be constructed only entirely in caller stack frame.

Another issue is ~S() behaviour:

// a.hpp
struct A {};
struct B {};
struct C { A a; B b; ~C(); };
// a.cpp
#include "a.hpp"
~C() { /* ... */; }
// main.cpp
#include "a.hpp"
A callee()
{
    C c;
    return c.a;
} // How to destruct c partially, having the user defined ~C() in another TU?
// Even if destructor is inline and its body is visible,
// how to automatically change its logic properly?
// It is impossible in general case.
void caller() { A a = callee(); }


来源:https://stackoverflow.com/questions/33344259/does-rvo-work-on-object-members

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!