c ++ 11 — Может ли возврат заключенного в скобки инициализатора привести к копированию в C ++?

Пример:

struct s { int a; };

s func() { return {42}; }

int main() {
s new_obj = func(); // line 6
(void) new_obj;
return 0;
}

Это работает. Теперь, что произойдет, если мы предположим, что наш компилятор не поддерживает RVO?

  1. func возвращает структуру s, так {42} должен быть преобразован в s, затем возвращается и, наконец, копируется в new_obj в строке 6.
  2. func возвращает список инициализатора, поэтому глубокая копия невозможна.

Что говорит язык? Можете ли вы дать доказательство?

Примечание: я знаю, что это не кажется полезным в этом примере, но для возврата очень большого, постоянного размера std::arrays, я не хочу полагаться на RVO.

7

Решение

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

#include <iostream>

struct foo {
foo(int) {}
foo(const foo&) { std::cout << "copy\n"; }
foo(foo&&)      { std::cout << "move\n"; }
};

foo f() {
//return 42;
return { 42 };
}

int main() {
foo obj = f();
(void) obj;
}

При компиляции с gcc 4.8.1 с -fno-elide-constructors для предотвращения RVO вывод

move

Если в f тогда используется оператор возврата без фигурных скобок, а затем вывод

move
move

Без RVO происходит следующее. f должен создать временный объект типа fooдавайте назовем это ret, чтобы быть возвращенным.

Если return { 42 }; используется, то ret является непосредственный инициализируется из значения 42, Таким образом, конструктор копирования / перемещения до сих пор не вызывался.

Если return 42; используется, то еще один временный, давайте назовем это tmp напрямую инициализируется из 42 а также tmp перемещен, чтобы создать ret, Следовательно, один конструктор перемещения был вызван до сих пор. (Заметить, что tmp это ценность и foo имеет конструктор перемещения. Если конструктора перемещения не было, вызывался бы конструктор копирования.)

Сейчас ret является значением и используется для инициализации obj, Следовательно, конструктор перемещения вызывается для перемещения из ret в obj, (Опять же, в некоторых случаях вместо этого можно вызвать конструктор копирования.) Следовательно, либо один (для return { 42 };) или два (для return 42;) ходы случаются.

Как я уже сказал в своем комментарии к вопросу ОП, этот пост очень актуален:
помощник по построению make_XYZ, разрешающий RVO и вывод типа, даже если XZY имеет ограничение на отсутствие копирования. Особенно отличный ответ от
Р. Мартиньо Фернандес
.

5

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

Других решений пока нет …

По вопросам рекламы [email protected]