Подтвердить что ты не робот

Есть ли какая-то тонкая разница между добавлением "=" в списки инициализаторов?

Есть ли какая-то тонкая разница между этими двумя способами инициализации переменных в С++ 11?

  • vector<double> v { 0.0, 1.1, 2.2, 3.3 };

  • vector<double> v = { 0.0, 1.1, 2.2, 3.3 };

Может ли последний использоваться для всех тех же случаев, что и первый?

Stroustrup утверждает в TCPL4ED, что первый способ является единственным, который может использоваться в каждом контексте и, следовательно, рекомендует его. Позже он, кажется, подразумевает, что второй - это просто другой способ написать первый.

4b9b3361

Ответ 1

1.

vector<double> v { 0.0, 1.1, 2.2, 3.3 };

Является ли инициализация прямого списка. Это означает, что он инициализируется конструктором, принимающим список инициализаторов.

Конструктор:

vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );

2.

vector<double> v = { 0.0, 1.1, 2.2, 3.3 };

Является ли инициализация списка копий.


Стандарт довольно ясен:

8.5.4 List-initialization [dcl.init.list]

Инициализация списка - это инициализация объекта или ссылки из списка с привязкой-init. Такой инициализатор называется списком инициализаторов, а разделяемые запятыми инициализаторы-предложения списка называются элементами списка инициализаторов. Список инициализаторов может быть пустым. Инициализация списка может возникать в контекстах с прямой инициализацией или copyinitialization; инициализация списка в контексте прямой инициализации называется инициализацией прямого списка, а инициализация списка в контексте инициализации копирования называется copy-list-initialization. [Примечание: инициализация списка можно использовать:

  • как инициализатор в определении переменной

[...]

Пример:

  std::complex<double> z{1,2};

  [...]

  std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };

Для разницы между обоими мы должны пойти немного дальше:

13.3.1.7 Инициализация с помощью инициализации списка [over.match.list]

  • Для инициализации прямого списка, кандидатские функции - все конструкторы класса T.
  • Для инициализации списка копий, кандидатские функции - все конструкторы T. Однако, если выбран конструктор explicit, инициализация плохо сформирована. [Примечание. Это ограничение применяется только в том случае, если эта инициализация является частью окончательного результата разрешения перегрузки - примечание конца)

Ответ 2

Целью равномерной инициализации было (частично) устранить разницу между этими двумя конструкциями. Чтобы гарантировать, что они будут иметь одинаковую функциональность.

К сожалению, они потерпели неудачу. Существует только одно различие между инициализацией прямого списка (т.е.: T t{...}) и инициализацией списка копий (т.е.: T t = {...}). Вам не нужен конструктор copy/move для инициализации списка копий (несмотря на имя); в разделе 8.5.4 не указано это как требование. Нет никакого понятия о временном строительстве, которое могло бы быть устранено. Они имеют одинаковое поведение, за исключением:

Инициализация копирования-списка не будет выполнена, если она выберет конструктор explicit. Это единственное различие.

Трудно дать цитату из спецификации, потому что есть только 3 упоминания о том, что такое инициализация списка копий. Один из них приведен в 8.5.4, где он определяет, что инициализация списка копий является формой инициализации списка, а другая заявляет, что для возврата в список бит-init-list используется инициализация списка копий. И последнее в 13.3.1.7, где указано указанное выше исключение:

В инициализации списка копий, если выбран конструктор explicit, инициализация плохо сформирована.

Итак, единственная разница.

Ответ 3

Единственное различие, которое приходит в голову, состоит в том, что первая форма может быть использована, даже если конструктор объявлен explicit, а второй не может. Извините, но @Timothy Shields и @Tomek ошибочны, оба утверждения представляют собой прямые инициализации, а не инициализацию копии.

Ответ 4

Я думаю, это похоже на случай с нормальными конструкторами (т.е. не используя списки инициализаторов). Первым выбором является построение с использованием (в данном случае) конструктора, использующего конструктор списка инициализаторов. Другой выбор - временный, созданный с помощью конструктора списка инициализаторов, а затем переменная копируется из этого. Временно уходит. Обратите внимание, что последний обычно пропускает временное и уничтожает копии как (N) RVO, но последнее требует, чтобы ваш класс имел доступный конструктор копирования в точке определения. По крайней мере, это то, что я помню из С++ 03, оно МОЖЕТ быть изменено в С++ 11.