Класс должен иметь допустимый конструктор копирования или перемещения, чтобы любой из этих синтаксисов был допустимым:
C x = factory();
C y( factory() );
C z{ factory() };
В C++03 было довольно часто полагаться на исключение копирования, чтобы компилятор не касался конструктора копирования. У каждого класса есть допустимый конструктор копирования signature независимо от того, существует ли определение.
В C++11 некопируемый тип должен определять C( C const & ) = delete;
, что делает любую ссылку на функцию недействительной независимо от использования (то же самое для неперемещаемого типа). (С++ 11 §8.4.3/2). GCC, например, будет жаловаться при попытке вернуть такой объект по значению. Copy elision перестает помогать.
К счастью, у нас также есть новый синтаксис для выражения намерения вместо того, чтобы полагаться на лазейку. Функция factory
может возвращать braced-init-list для создания временного результата на месте:
C factory() {
return { arg1, 2, "arg3" }; // calls C::C( whatever ), no copy
}
Изменить. Если есть какие-либо сомнения, этот оператор return
анализируется следующим образом:
- 6.6.3/2: «Операция возврата со списком инициализации в фигурных скобках инициализирует объект или ссылку, которые должны быть возвращены из функции с помощью инициализации списка копирования (8.5.4) из указанного списка инициализаторов».
- 8.5.4/1: «инициализация списка в контексте инициализации копирования называется инициализацией списка копирования». ¶3: «если T является типом класса, учитываются конструкторы. Перечисляются применимые конструкторы, и лучший из них выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7)».
Пусть вас не вводит в заблуждение название copy-list-initialization. 8.5:
13: Форма инициализации (с использованием круглых скобок или
=
) обычно не имеет значения, но имеет значение, когда инициализатор или инициализируемый объект имеет тип класса; увидеть ниже. Если инициализируемый объект не имеет типа класса, список-выражений в инициализаторе в скобках должен быть одним выражением.14: Инициализация, которая происходит в форме
T x = a;
, а также при передаче аргументов, возврате функции, создании исключения (15.1), обработке исключения (15.3) и инициализации агрегатного члена (8.5.1), называется copy- инициализация.
Как копирующая-инициализация, так и ее альтернатива, прямая-инициализация, всегда зависят от инициализации-списка, когда инициализатором является список-инициализации в фигурных скобках. Добавление =
не имеет семантического эффекта, что является одной из причин, по которой инициализация списком неофициально называется униформной инициализацией.
Есть отличия: прямая инициализация может вызвать явный конструктор, в отличие от инициализации копированием. Копирование-инициализация инициализирует временный объект и копирует его для инициализации объекта при конвертации.
Спецификация инициализации-списка-копирования для операторов return { list }
просто указывает точный эквивалентный синтаксис, который будет temp T = { list };
, где =
обозначает инициализацию копирования. Это не означает, что вызывается конструктор копирования.
-- Завершить редактирование.
Затем результат функции может быть получен в ссылке rvalue, чтобы предотвратить копирование временного файла в локальный:
C && x = factory(); // applies to other initialization syntax
Вопрос в том, как инициализировать нестатический член из фабричной функции, возвращающей некопируемый, неперемещаемый тип? Трюк со ссылками не работает, потому что элемент ссылки не продлевает время жизни временного элемента.
Обратите внимание: я не рассматриваю агрегатную инициализацию. Речь идет об определении конструктора.
struct CounterExample { }; CounterExample ce; CounterExample ce1{ ce }; /* ill-formed */
. - person Johannes Schaub - litb   schedule 17.06.2012struct CounterExample { CounterExample(CounterExample const&) = delete; int a; operator int() { return 42; } }; CounterExample factory() { return {}; } CounterExample c{factory()}; /* fine! */
- person Johannes Schaub - litb   schedule 17.06.2012T && make_T() { return std::move(T(constructor_argument)); }
. Иstd::move
, и&&
необходимы для его компиляции. Однако я не могу скомпилировать, когда пытаюсь выполнитьmember_variable(make_T())
илиmember_variable(std::move(make_T()))
. Это где GCC неверен? В моем коде используются функции С++ 11, которые clang не поддерживает, поэтому я не могу проверить это таким образом. - person David Stone   schedule 13.08.2012