Законна ли эта программа?
struct X { X(const X &); };
struct Y { operator X() const; };
int main() {
X{Y{}}; // ?? error
}
После n2672, и с поправками дефект 978, 13.3.3.1 [over.best.ics] имеет:
4 — Тем не менее, при рассмотрении аргумента конструктора или пользовательской функции преобразования, которая является кандидатом […] по 13.3.1.7 […], когда список инициализатора имеет ровно один элемент и преобразование в некоторый класс X или ссылка на (возможно, cv-квалифицированная) X рассматривается для первого параметра конструктора X […], рассматриваются только стандартные последовательности преобразования и последовательности преобразования эллипса.
Это кажется довольно извращенным; это приводит к тому, что указание преобразования с использованием приведения в список инициализации недопустимо:
void f(X);
f(Y{}); // OK
f(X{Y{}}); // ?? error
Насколько я понимаю n2640, Предполагается, что списочная инициализация способна заменить все виды использования прямой инициализации и инициализации копирования, но, похоже, нет способа создать объект типа X
от объекта типа Y
используя только инициализацию списка:
X x1(Y{}); // OK
X x2 = Y{}; // OK
X x3{Y{}}; // ?? error
Является ли это фактическим намерением стандарта; если нет, то как это читать или читать?
Первоначальная цель 13.3.3.1p4 состоит в том, чтобы описать, как применять требование в 12.3p4, которое:
4 — Не более одного пользовательского преобразования (конструктор или функция преобразования) неявно применяется к одному значению.
До дефект 84, 13.3.3.1p4 было почти чисто информативно:
4 — В контексте инициализации пользовательским преобразованием (т. Е. При рассмотрении аргумента пользовательской функции преобразования; см. 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv] ), разрешены только стандартные последовательности преобразования и последовательности преобразования эллипса.
Это связано с тем, что в п. 2 пункта 13.3.1.4 и в п. 13.3.1.5p1b1 функции-кандидаты ограничиваются функциями класса S
урожайный тип T
, где S
тип класса выражения инициализатора и T
тип инициализируемого объекта, поэтому нет никакой широты для вставки другой последовательности преобразования, определенной пользователем. (13.3.1.4p1b1 — другое дело; см. Ниже).
Дефект 84 отремонтирован auto_ptr
лазейка (т.е. auto_ptr<Derived> -> auto_ptr<Base> -> auto_ptr_ref<Base> -> auto_ptr<Base>
через две функции преобразования и конструктор преобразования) путем ограничения последовательностей преобразования, допустимых для одного параметра конструктора на втором этапе инициализации класса copy (здесь конструктор auto_ptr<Base>
принятие auto_ptr_ref<Base>
, запрещающий использование функции преобразования для преобразования ее аргумента из auto_ptr<Base>
):
4 — Тем не менее, при рассмотрении аргумента пользовательской функции преобразования, которая является кандидатом в 13.3.1.3 [over.match.ctor] при вызове для копирования временного объекта на втором этапе инициализации класса copy, или согласно 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv] или 13.3.1.6 [over.match.ref] во всех случаях разрешены только стандартные последовательности преобразования и последовательности преобразования эллипса.
n2672 затем добавляет:
[…] на 13.3.1.7 [over.match.list] при передаче списка инициализаторов в качестве одного аргумента или когда список инициализаторов содержит ровно один элемент и преобразование в некоторый класс X или ссылку на (возможно, cv-qualified) X рассматривается как первый параметр конструктора X, […]
Это явно запутано, поскольку единственными преобразованиями, которые являются кандидатами в соответствии с 13.3.1.3 и 13.3.1.7, являются конструкторы, а не функции преобразования. Дефект 978 исправляет это:
4 — Тем не менее, при рассмотрении аргумента конструктора или пользовательской функции преобразования […]
Это также делает 13.3.1.4p1b1 совместимым с 12.3p4, так как в противном случае это позволило бы неограниченное применение конвертирующих конструкторов при инициализации копирования:
struct S { S(int); };
struct T { T(S); };
void f(T);
f(0); // copy-construct T by (convert int to S); error by 12.3p4
Вопрос в том, что означает язык, относящийся к 13.3.1.7. X
выполняется копирование или перемещение, поэтому язык исключает применение пользовательского преобразования для получения X
аргумент. std::initializer_list
не имеет функций преобразования, поэтому язык должен быть предназначен для применения к чему-то другому; если он не предназначен для исключения функций преобразования, он должен исключать конструкторы преобразования:
struct R {};
struct S { S(R); };
struct T { T(const T &); T(S); };
void f(T);
void g(R r) {
f({r});
}
Есть два доступных конструктора для инициализации списка; T::T(const T &)
а также T::T(S)
, Исключая конструктор копирования из рассмотрения (поскольку его аргумент должен быть преобразован через определяемую пользователем последовательность преобразования), мы гарантируем, что только правильный T::T(S)
конструктор считается. В отсутствие этого языка инициализация списка была бы неоднозначной. Передача списка инициализаторов как единственного аргумента работает аналогично:
struct U { U(std::initializer_list<int>); };
struct V { V(const V &); V(U); };
void h(V);
h({{1, 2, 3}});
Редактировать: и пройдя через все это, я нашел обсуждение от Йоханнес Шауб что подтверждает этот анализ:
Это предназначено, чтобы отделить конструктор копии для инициализации списка
поскольку нам разрешено использовать вложенные пользовательские преобразования, мы
всегда может привести к неоднозначному второму пути преобразования, сначала вызвав
конструктор копирования, а затем делать то же самое, что мы делали для других
преобразования.
ОК, чтобы отправить отчет о дефекте. Я собираюсь предложить разделить 13.3.3.1p4:
4 — Однако при рассмотрении аргумента конструктора или пользовательской функции преобразования, которая является кандидатом:
- 13.3.1.3 [over.match.ctor] при вызове для копирования временного файла на втором шаге инициализации класса copy, или
- 13.3.1.4 [over.match.copy], 13.3.1.5 [over.match.conv] или 13.3.1.6 [over.match.ref] во всех случаях,
рассматриваются только стандартные последовательности преобразования и последовательности преобразования эллипса; при рассмотрении первого аргумента конструктора класса
X
это является кандидатом по 13.3.1.7 [over.match.list] при передаче списка инициализаторов в качестве единственного аргумента или когда список инициализаторов содержит ровно один элемент, определяемое пользователем преобразование вX
или ссылка на (возможно резюме-квалифицированный)X
рассматривается только в том случае, если пользовательское преобразование определено функцией преобразования. [Замечания: поскольку в последовательности неявного преобразования в контексте инициализации списка допускается более одного пользовательского преобразования, это ограничение необходимо для обеспечения того, чтобы конструктор преобразованияX
вызывается с одним аргументомa
это не типX
или тип, полученный изX
, не является двусмысленным в отношении конструктораX
звонил с временнымX
сам объект построен изa
, — конечная нота]
Версия clang 3.1, поставляемая с XCode 4.4, согласуется с вашей интерпретацией и отклоняет X{Y{}};
, Как и я, после перечитывания соответствующих частей стандарта несколько раз, FWIW.
Если я изменю X
конструктор для принятия двух аргументов, оба типа const X&
Clang принимает заявление Y y; X{y,y}
, (Это сбой, если я пытаюсь X{Y{},Y{}}
…). Кажется, это согласуется с 13.3.3.1p4, который требует, чтобы пользовательские преобразования были пропущены только для случая с одним элементом.
Похоже, что ограничение к стандартным последовательностям и последовательностям преобразования эллипса было добавлено изначально только в тех случаях, когда другой пользовательское преобразование уже произошло. Или, по крайней мере, так я читаю http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#84.
Интересно, как стандарт осторожно применяет ограничение только ко второму этапу инициализации копирования, который копирует из временного файла, который уже имеет правильный тип (и был получен потенциально через пользовательское преобразование!). Тем не менее, для инициализации списка, похоже, не существует аналогичного механизма …