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

В C ++ 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)’

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

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

18

Решение

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

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

§13.3.1.7

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


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

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

cppreference:

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

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

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

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

T объект знак равноarg1, арг2, }; (10)

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

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

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

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

12

Другие решения

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

Foo foo = 42;

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

12

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

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

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

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

7
По вопросам рекламы [email protected]