Пример:
struct s { int a; };
s func() { return {42}; }
int main() {
s new_obj = func(); // line 6
(void) new_obj;
return 0;
}
Это работает. Теперь, что произойдет, если мы предположим, что наш компилятор не поддерживает RVO?
func
возвращает структуру s
, так {42}
должен быть преобразован в s
, затем возвращается и, наконец, копируется в new_obj
в строке 6.func
возвращает список инициализатора, поэтому глубокая копия невозможна.Что говорит язык? Можете ли вы дать доказательство?
Примечание: я знаю, что это не кажется полезным в этом примере, но для возврата очень большого, постоянного размера std::array
s, я не хочу полагаться на RVO.
Рассмотрим следующий пример:
#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 имеет ограничение на отсутствие копирования. Особенно отличный ответ от
Р. Мартиньо Фернандес.
Других решений пока нет …