18

В C++ 11 мы можем инициализировать в классе с помощью «скользящего или равного инициализатора» (слова от стандарта), как это:C++ 11: инициализация in-class с «= {}» не работает с явным конструктором

struct Foo 
{ 
    /*explicit*/ Foo(int) {} 
}; 

struct Bar 
{ 
    Foo foo = { 42 }; 
}; 

Но если мы не-комментарий explicit, он больше не компилирует. GCC 4.7 и 4.9 говорят об этом:

error: converting to ‘Foo’ from initializer list would use explicit constructor ‘Foo::Foo(int)’ 

Я нашел это удивительным. Действительно ли это намерение стандарта C++ 11, что этот код не компилируется?

Снятие = фиксирует это: Foo foo { 42 };, но я лично считаю это труднее объяснить людям, которые были использованы в форме с = на протяжении десятилетий, и так как стандарт относится к «распорки или равно-инициализаторе» это неясно, почему хороший старый способ не работает в этом сценарии.

+2

Синтаксис '{}' инициализатора является своего рода хаком, и у него есть куча странных угловых случаев, подобных этому –

+0

Я думал, что вам нужно использовать двойные фигурные скобки - попробуйте с помощью 'Foo foo = {{42}};' –

+0

@CarlBurnett: двойные фигурные скобки, похоже, делают то же самое, что и отдельные фигурные скобки - все еще сломанные. –

ответ

12

Я не могу объяснить причины этого, но я могу повторить очевидное.

Я нашел это удивительным. Действительно ли это намерение стандарта C++ 11 , что этот код не компилируется?

§13.3.1.7

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


Извлечение = фиксирует это: Foo foo { 42 };, но я лично считаю это сложнее объяснить людям, которые были использованы в форме с = для десятилетий, и так как стандарт относится к «brace-or-equal-initializer» не очевидно, почему старый старый способ не работает в этом сценарии.

Foo foo { 42 } является direct initialization, в то время как знак равенства (со скобами) делает его copy-list-initialization. Другой ответ объясняет, что из-за сбоя компиляции для copy-initialization (знак равенства без фигурных скобок), неудивительно, что он также терпит неудачу для инициализации списка копий, но они не работают по разным причинам.

cppreference:

Прямой инициализация более снисходительная, чем копия инициализация: копия инициализация рассматривает только не явные конструктор и определяемого пользователь функции преобразования, в то время как прямая инициализация рассматривает все конструктор и неявные последовательности преобразования.

И их страница на explicit specifier:

Определяет конструкторы и (с C++ 11) операторов преобразования , которые не позволяют неявные преобразования или копирования-инициализации.

С другой стороны, для копирования списка инициализации:

T объекта = {arg1, арг2, ...}; (10)

10) на правой руке стороне от знака равенства (аналогично для копирования-инициализации)

  • В противном случае, конструкторы Т рассматриваются в два этапа:

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

Как обсуждалось в What could go wrong if copy-list-initialization allowed explicit constructors?, компиляция завершилась неудачно, потому что явный конструктор выбран, но не разрешен для использования.

7

widget w = {x};

Это называется «инициализация списка копирования». Это означает то же самое, что и виджет w {x}; за исключением того, что явные конструкторы не могут использоваться. Гарантируется, что вызывается только один конструктор.

От http://herbsutter.com/2013/05/09/gotw-1-solution/

Смотрите остальные статьи для более подробного обсуждения различных способов, вы можете инициализировать объект.

11

Если Foo(int) является explicit, то это не будет компилировать также:

Foo foo = 42; 

Так что для «людей, которые были использованы в форме с = десятилетиями» это не будет сюрпризом, что форма с {} тоже не скомпилируется.

+5

'Foo foo = 42' * неявно * создает' Foo' prvalue для инициализации 'foo' с. Это не относится к 'Foo foo = {42}', который * не * неявно создает 'Foo'. – dyp

+0

Я с @dyp: Я понимаю, почему 'Foo foo = 42' не будет работать, и я в порядке с ним. Я все еще смущен, почему «Foo foo = {42}» ​​не работает ... похоже, из другого ответа здесь, что это может быть то, что стандартное предназначение или, по крайней мере, ожидалось, но кажется ... плохим. –