Концептуально ли инициализация списка копирования вызывает copy ctor?

До C++11 мы можем выполнить инициализацию копирования, написав что-то вроде A a = 1; что более или менее эквивалентно A a = A(1);, То есть сначала создается временный объект, а затем вызывается копия ctor. Независимо от исключения копирования, это должно быть так концептуально, и копия ctor должна быть доступна.

При инициализации списка в C++11 мы можем выполнить инициализацию списка копирования, написав A a = {1, 2};, На мой взгляд, это должно быть более или менее эквивалентно A a = A(1, 2);, Тем не менее, на GCC и лязг, A a = {1, 2} компилируется, даже когда ctor для копирования и перемещения недоступен (объявляется как private). Еще, A a = 1; не компилируется в GCC или clang, если соответствующий ctor копирования / перемещения недоступен. Так, A a = {1, 2}; кажется более или менее эквивалентным A a{1, 2}; которая является прямой инициализацией списка. Разница между этим и реальной прямой инициализацией списка заключается в том, что A a = {1, 2}; не компилируется, если ctor, который принимает два целых числа, является явным. В этом аспекте A a = {1, 2}; напоминает инициализацию копирования.

Итак, мой вопрос: какова точная семантика выражений типа A a = {1, 2}; концептуально? Концептуально, копия elision не стоит на пути.

1 ответ

Решение

Стандарт описывает это довольно хорошо; [Dcl.init.list]/3:

Инициализация списка объекта или ссылки типа T определяется следующим образом:

  • [...]
  • В противном случае, если T является типом класса, конструкторы рассматриваются. Применимые конструкторы перечисляются, и лучший выбирается через разрешение перегрузки (13.3, 13.3.1.7). Если для преобразования какого-либо из аргументов требуется сужающее преобразование (см. Ниже), программа является некорректной.

[over.match.list] (выделено мной):

Когда объекты неагрегированного класса T инициализируются списком (8.5.4), разрешение перегрузки выбирает конструктор в два этапа:

  • Первоначально функции-кандидаты являются конструкторами списка инициализаторов (8.5.4) класса T и список аргументов состоит из списка инициализаторов как единственного аргумента.

  • Если жизнеспособный конструктор списка инициализаторов не найден, разрешение перегрузки выполняется снова, где все функции-кандидаты являются конструкторами класса. T и список аргументов состоит из элементов списка инициализатора.

Если список инициализаторов не имеет элементов, а T имеет конструктор по умолчанию, первая фаза опускается.
В инициализации копирования списка, если explicit конструктор выбран, инициализация некорректна.

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

Это одно из преимуществ инициализации списка: это не требует наличия специальной функции-члена, которая в любом случае не будет использоваться.

Другие вопросы по тегам