Ответ 1
Вероятно, поведение новой инициализации списка копий было определено как "хорошее" и последовательное, но "странное" поведение старой инициализации копирования не могло быть изменено из-за обратной совместимости.
Как вы можете видеть, правила для инициализации списка в этом разделе идентичны для прямых и копируемых форм.
Разница, связанная с explicit
, описана только в главе о разрешении перегрузки. Но для традиционной инициализации прямые и скопированные формы не идентичны.
Традиционные и скользящие инициализации определяются отдельно, поэтому всегда есть вероятность некоторых (возможно, непреднамеренных) тонких различий.
Различия, которые я вижу из выдержек стандарта:
1. Уже упомянутые различия
- сужение конверсий запрещено.
- возможны несколько аргументов
-
привязанный синтаксис предпочитает конструкторы списка инициализаторов, если они присутствуют:
struct A { A(int i_) : i (i_) {} A(std::initializer_list<int> il) : i (*il.begin() + 1) {} int i; } A a1 = 5; // a1.i == 5 A a2 = {5}; // a2.i = 6
2. Различное поведение для агрегатов
Для агрегатов вы не можете использовать скопированный конструктор-копию, но можете использовать традиционный.
struct Aggr
{
int i;
};
Aggr aggr;
Aggr aggr1 = aggr; // OK
Aggr aggr2 = {aggr}; // ill-formed
3. Различное поведение для инициализации ссылок в присутствии оператора преобразования
Инициализация скобок не может использовать операторы преобразования в ссылочный тип
struct S
{
operator int&() { return some_global_int;}
};
int& iref1 = s; // OK
int& iref2 = {s}; // ill-formed
4. Некоторые тонкие различия в инициализации объекта типа класса объектом другого типа
Эти различия отмечены [*] в выдержках Стандарта в конце этого ответа.
- В старой инициализации используется понятие пользовательских последовательностей преобразования (и, в частности, требуется наличие конструктора копирования, как было упомянуто)
- Инициализация скобок просто выполняет разрешение перегрузки среди применимых конструкторов, то есть инициализация скобок не может использовать операторы преобразования в тип класса
Эти различия несут ответственность за некоторые не очень очевидные (для меня) случаи вроде
struct Intermediate {};
struct S
{
operator Intermediate() { return {}; }
operator int() { return 10; }
};
struct S1
{
S1(Intermediate) {}
};
S s;
Intermediate im1 = s; // OK
Intermediate im2 = {s}; // ill-formed
S1 s11 = s; // ill-formed
S1 s12 = {s}; // OK
// note: but brace initialization can use operator of conversion to int
int i1 = s; // OK
int i2 = {s}; // OK
5. Разница в разрешении перегрузки
- Различная трактовка явных конструкторов
См. 13.3.1.7 Инициализация с помощью инициализации списка
В инициализации списка копий, если выбран конструктор
explicit
, инициализация плохо сформирована. [Примечание: это отличается от других ситуаций (13.3.1.3, 13.3.1.4), где только преобразования конструкторов рассматриваются для инициализации копии. Это ограничение применяется только если эта инициализация является частью конечного результата перегрузки разрешающая способность. - конечная нота]
Если вы видите больше различий или каким-то образом корректируете мой ответ (включая грамматические ошибки), пожалуйста, сделайте.
Вот соответствующие (но длинные) выдержки из текущего проекта стандарта С++ (я не нашел способ скрыть их под спойлером): < ш > Все они находятся в главе 8.5 "Инициализаторы"
8.5 Инициализаторы
Если инициализатор представляет собой (не заключенный в скобки) бит-init-list, объект или ссылка инициализируется списком (8.5.4).
Если тип назначения является ссылочным типом, см. 8.5.3.
Если тип назначения - это массив символов, массив
char16_t
, массивchar32_t
или массивwchar_t
, а инициализатор - это строковый литерал, см. 8.5.2.Если инициализатор
()
, объект значение инициализации.В противном случае, если тип назначения является массивом, программа плохо сформирована.
Если тип адресата (возможно, cv-qualified) тип класса:
Если инициализация прямая инициализация, или если это копирование, где cv-неквалифицированная версия типа источника - это тот же класс, что или производный класс, класс назначения, конструкторы считается. Соответствующие конструкторы перечислены (13.3.1.3) и лучший выбирается с помощью разрешения перегрузки (13.3). выбранный конструктор вызывается для инициализации объекта, причем выражение инициализатора или список выражений как его аргумент (ы). Если нет конструктор, или разрешение перегрузки неоднозначно, инициализация плохо сформирована.
[*]В противном случае (т.е. Для оставшиеся случаи инициализации копии), пользовательское преобразование последовательности, которые могут конвертировать из типа источника в место назначения type или (когда используется функция преобразования) в производный класс из них перечислены, как описано в 13.3.1.4, и лучший из них выбирается с помощью разрешения перегрузки (13.3). Если преобразование не может быть сделано или неоднозначно, инициализация плохо сформирована. Функция selected вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов инициализирует временную cv-неквалифицированная версия типа назначения. Временным является prvalue. Результат вызова (который является временным для конструктор) затем используется для прямой инициализации, согласно правил выше, объект, который является местом назначения копирования инициализации. В некоторых случаях допускается реализация для устранения копирования, присущего этой прямой инициализации, посредством построение промежуточного результата непосредственно в объект, являющийся инициализируется; см. 12.2, 12.8.
В противном случае, если тип источника является (возможно, cv-qualit), функции преобразования считается. Применяемые функции преобразования перечислены (13.3.1.5), а лучший выбирается с помощью разрешения перегрузки (13.3). Выбранное пользователем преобразование вызывается для преобразования выражение инициализатора в инициализированный объект. Если преобразование не может быть выполнено или неоднозначно, инициализация плохо сформирован.
В противном случае начальное значение объекта инициализировано (возможно, преобразованное) значение инициализатора выражение. Стандартные преобразования (раздел 4) будут использоваться, если необходимо, чтобы преобразовать выражение инициализатора в cv-unqualified версия типа назначения; без пользовательских преобразований считается. Если преобразование не может быть выполнено, инициализация плохо сформирован.
8.5.3 Ссылки...
8.5.4 Инициализация списка
Список-инициализация объекта или ссылки типа T определяется как следует:
Если
T
является агрегатом, агрегатная инициализация (8.5.1).В противном случае, если в списке инициализаторов нет элементы и
T
- это тип класса с конструктором по умолчанию, объект инициализируется значением.В противном случае, если
T
является специализациейstd::initializer_list<E>
объект prvalueinitializer_list
построенный, как описано ниже, и используется для инициализации объекта в соответствии с правилами инициализации объекта из класса тот же тип (8.5).[*] В противном случае, если
T
- тип класса, конструкторы. Применимые конструкторы перечислены, а лучший выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7). Если требуется сужение конверсии (см. Ниже), чтобы конвертировать любой из аргументов, программа плохо сформирована.В противном случае, если в списке инициализаций имеется один элемент типа
E
и либоT
не является ссылочным типом, либо его ссылочный тип ссылка, связанная сE
, объект или ссылка инициализируются из этот элемент; если сужение конверсии (см. ниже) требуется для преобразуйте элемент вT
, программа плохо сформирована.В противном случае, если
T
является ссылочным типом, временным значением типа, на который ссылаетсяT
инициализируется или инициализируется списком-списком, в зависимости от вид инициализации для ссылки, и ссылка привязана к этому временному. [Примечание. Как обычно, привязка не будет выполнена, и программа плохо сформирована, если ссылочный тип является ссылкой lvalue на неконстантный тип. - end note ]В противном случае, если список инициализаторов не имеет элементов, объект инициализируется значением.
В противном случае программа плохо сформирована.