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/
более подробное обсуждение различных способов инициализации объекта см. В остальной части статьи.