Работает ли RVO на членах объекта?

Учтите следующее:

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

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

auto a = foo();

Будет p.first копироваться, перемещаться или RVO-ed?

12

Решение

Я нашел в Visual Studio 2010 и в GCC-5.1 RVO является не применяется (см. например http://coliru.stacked-crooked.com/a/17666dd9e532da76).

Соответствующий раздел стандарта — 12.8.31.1 [class.copy]. В нем говорится, что разрешение на копирование разрешено (мое выделение):

в операторе возврата в функции с типом возврата класса, когда выражение является имя энергонезависимого автоматического объекта (кроме параметра функции или переменной, введенной в объявлении исключения обработчика ([exception.handle])) с тем же типом (игнорируя квалификацию cv), что и тип возврата функции, операция копирования / перемещения может быть опущена путем создания автоматического объекта непосредственно в возвращаемое значение функции

поскольку p.first это не название объекта, RVO запрещено.

9

Другие решения

Просто чтобы добавить немного больше топлива, как бы это работало, если бы RVO были в игре? Звонивший поставил экземпляр A где-то в памяти, а затем звонит foo назначить ему (даже лучше, давайте предположим, что это A был частью более крупной структуры, и давайте предположим, что она правильно выровнена, так что следующий элемент структуры находится сразу после этого экземпляра A). Предполагая, что RVO были в игре, first часть p находится там, где этого хотел абонент, но где int то есть second занять место? Это должно идти сразу после случая A чтобы сохранить pair работает правильно, но в исходном расположении, есть еще один член сразу после этого экземпляра A,

Я ожидаю, что RVO не будет происходить в этом месте, поскольку вы возвращаете только часть более крупного объекта. Движение может произойти как first пришлось бы оставить в разрушаемом состоянии.

9

@atkins попал сюда первым с ответом. Просто добавьте эту небольшую тестовую программу, которая может оказаться полезной в будущем при отслеживании поведения перемещения / назначения

#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;
}

пример вывода:

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

Рассмотрим следующий код:

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;
}

«Частичное» RVO должно привести к чрезмерному вздутию живота в стеке, потому что (я думаю) S может быть построен только целиком в кадре стека вызывающего.

Еще одна проблема ~S() поведение:

// 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(); }
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector