C++11: in-class initializaton с "= {} " не работает с явным конструктором

в 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 }; но я лично считаю, что это труднее объяснить людям, которые были используется для формы с = в течение десятилетий, и поскольку стандарт ссылается на" скобку или равный инициализатор", не очевидно, почему старый добрый способ не работает в этом сценарии.

3 ответов


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

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

§13.3.1.7

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


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

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

cppreference:

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

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

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

С другой стороны, для инициализации copy-list:

Т = {арг1, аргумент2, ...}; (10)

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

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

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

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


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

Foo foo = 42;

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


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

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

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

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