Синтаксис инициализации объекта в C++ (T obj = {} vs T obj{})

В чем разница между двумя формами инициализации, T obj = {…} и T obj{…}?
Сначала я думал, что T obj = {…} — это сокращение от T obj = T{…}, когда временный объект копируется в наш новый объект. Это, хотя и не выполняет конструктор копирования (copy elision), требует его существования и доступа к этому. Но когда я заблокировал доступ к конструктору копирования в этом конкретном классе, сделав конструктор закрытым, ошибки не было.
Это означает, что механизм копирования не задействован. Итак, какова функция символа '='?
Я сослался на следующий вопрос, но был недоволен отсутствием объяснения:
Является ли унифицированная инициализация C++11 заменой синтаксиса старого стиля?

EDIT: Аналогично, есть ли разница между int arr[]{…} и int arr[] = {…}? Я прошу об этом, чтобы посмотреть, смогу ли я выявить контраст между юниформ-инициализацией и инициализацией списка.


person Vishal Subramanyam    schedule 22.03.2020    source источник
comment
Какой стандарт вы используете? С++ 17 elision может объяснить ваши результаты. Хотя также обратите внимание, что = в инициализаторах часто вводит в заблуждение. По этой теме существует более одного вопроса; ваша книга также должна затрагивать эту тему.   -  person Asteroids With Wings    schedule 23.03.2020
comment
@AsteroidsWithWings, я использую C++17. Даже с elision единственное, что происходит, это то, что конструктор копирования не вызывается. Но его наличие и доступ к нему обязательны. Если я определяю конструктор копирования и скрываю его в классе, не говорю ли я компилятору, что операция копирования не поддерживается?   -  person Vishal Subramanyam    schedule 23.03.2020
comment
Он использует стандартный, а затем неявно сгенерированный конструктор перемещения.   -  person Александр Кушни&    schedule 23.03.2020
comment
@АлександрКушниренко, а в чем разница между двумя заявлениями, которые я написал?   -  person Vishal Subramanyam    schedule 23.03.2020
comment
@АлександрКушниренко Является ли T obj = {...} сокращением от T obj = T{...}?   -  person Vishal Subramanyam    schedule 23.03.2020
comment
Я только что написал фрагмент кода, и он все равно вызывает конструктор по умолчанию, так что я был неправ. Возможно, компилятор использует какие-то методы оптимизации в этом вопросе.   -  person Александр Кушни&    schedule 23.03.2020
comment
@АлександрКушниренко, я отключил все оптимизации в Visual Studio 2017, зная, что это может быть причиной. Это все еще происходит.   -  person Vishal Subramanyam    schedule 23.03.2020
comment
@Vishal Subramanyam, T obj = T{...} также не вызывает конструктор перемещения, поэтому я запутался.   -  person Александр Кушни&    schedule 23.03.2020
comment
Отвечает ли это на ваш вопрос: стандартное различие между инициализацией прямого списка и копированием"> stackoverflow.com/questions/13461027/   -  person M.M    schedule 23.03.2020
comment
Редактирование 2 было другим вопросом, пожалуйста, придерживайтесь 1 вопроса на вопрос. Я откатил правку. Если вы не можете ответить на этот вопрос, выполнив поиск других вопросов по инициализации на этом сайте, задайте новый вопрос.   -  person M.M    schedule 23.03.2020
comment
универсальная инициализация не является терминологией, используемой Стандартом.   -  person M.M    schedule 23.03.2020
comment
Я использую C++17. Даже с elision единственное, что происходит, это то, что конструктор копирования не вызывается. Но его наличие и доступ к нему обязательны Это неверно. Вы думаете о старой оптимизации, разрешенной предыдущими стандартами.   -  person Asteroids With Wings    schedule 23.03.2020
comment
@АлександрКушниренко Он использует конструктор перемещения по умолчанию, а затем неявно сгенерированный конструктор перемещения. Это неверно.   -  person Asteroids With Wings    schedule 23.03.2020


Ответы (2)


Они имеют почти точно такой же эффект:

  • T x = { 1, 2, 3 };
  • T x { 1, 2, 3 };

Технически версия с = называется copy-list-initialization, а другая версия — direct-list-initialization, но поведение обеих этих форм определяется списка-инициализации.

Различия:

  • Если copy-list-initialization выбирает конструктор explicit, код имеет неправильный формат.
  • If T is auto, then:
    • copy-list-initialization deduces std::initializer_list<Type_of_element>
    • direct-list-initialization допускает только один элемент в списке и выводит Type_of_element.

Дополнительная информация: Почему стандарт различает прямые -list-initialization и copy-list-initialization?


Если T является типом массива, то все вышесказанное остается в силе; поскольку инициализация списка массивов всегда является агрегатной инициализацией, конструктор никогда не выбирается, и поэтому две версии одинаковы во всех случаях.


T obj = T{...} (за исключением auto) точно такое же, как T obj{...}, начиная с C++17, т. е. прямая-инициализация-списка для obj. До C++17 существовала прямая-инициализация-списка временного файла, а затем копия-инициализация obj из временного файла.

person M.M    schedule 22.03.2020

Я думаю, что сравнение этих двух синтаксисов не является вашим настоящим вопросом.

Мне кажется, что вы ожидаете, что elision С++ 17 будет вести себя так же, как оптимизация до С++ 17, разрешенная стандартом и выполняемая многими реализациями.

В этой оптимизации, хотя вызов конструктора копирования можно было исключить, он должен был быть действительным и доступным.

Это не относится к исключению C++17.

Это настоящее упущение, при котором простое написание T{} на самом деле не создает T, а вместо этого говорит, что я хочу T, и фактическое временное материализуется, только если/когда это необходимо.

Излишние высказывания об этом факте эффективно схлопываются в одно, поэтому, несмотря на крик, я хочу T! Я хочу T! Я хочу T! Я хочу T! ребенок по-прежнему получает только один T в конце. ????

Итак, в C++17 T obj = T{...} буквально эквивалентно T obj{...}.

Это объясняет результаты, которые вы видите, и ваше замешательство.


Подробнее об этой функции можно прочитать на cppreference.com; вот фрагмент из верхней части страницы:

Обязательное исключение операций копирования/перемещения

При следующих обстоятельствах компиляторы должны опускать конструкцию копирования и перемещения объектов класса, даже если конструктор копирования/перемещения и деструктор имеют наблюдаемые побочные эффекты. Объекты создаются непосредственно в хранилище, куда они в противном случае были бы скопированы/перемещены. Конструкторы копирования/перемещения не обязательно должны присутствовать или быть доступными:

  • [..]
  • При инициализации объекта, когда выражением инициализатора является значение prvalue того же типа класса (без учета cv-квалификации), что и тип переменной [..]
person Asteroids With Wings    schedule 23.03.2020
comment
Кроме того, предполагая, что T является классом с конструктором, у которого есть параметры, T obj = {param1, param2} работает, вызывая правильный конструктор. Но T obj = (param1, param2) не работает. Почему это? У меня была путаница в отношении исключения, но ваш ответ прояснил это. Есть ли какой-нибудь ресурс, который подробно объясняет эти языковые конструкции? - person Vishal Subramanyam; 23.03.2020
comment
Но T obj = (param1, param2) не работает. Почему? Потому что этот синтаксис инициализации не поддерживается C++. Подробный ресурс: eel.is/c++draft - person Asteroids With Wings; 23.03.2020