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

С++ 11: инициализация в классе с "= {}" не работает с явным конструктором

В С++ 11 мы можем выполнить инициализацию в классе с помощью "скобки-или-равного-инициализатора" (слова из стандарта), например:

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)’

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

Удаление = исправляет его: Foo foo { 42 };, но я лично затрудняюсь объяснить людям, которые были использованы в форме с помощью = в течение десятилетий, и поскольку стандарт относится к "скобке или равный-инициализатор" не очевидно, почему старый старый способ не работает в этом сценарии.

4b9b3361

Ответ 1

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

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

§13.3.1.7

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


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

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

cppreference:

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

И их страница в явный спецификатор:

Задает конструкторы и (поскольку С++ 11) конверсия операторы, которые не допускают неявных преобразований или копирования инициализации.

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

T object = {arg1, arg2,...}; (10)

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

  • В противном случае конструкторы T рассматриваются в двух фазах:

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

Как обсуждалось в Что может пойти не так, если инициализация списка копий разрешила явные конструкторы?, компиляция завершилась неудачно, потому что явный конструктор выбран, но не разрешен б.

Ответ 2

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

Foo foo = 42;

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

Ответ 3

виджет w = {x};

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

Из http://herbsutter.com/2013/05/09/gotw-1-solution/

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