Следующий код довольно тривиален, и я ожидал, что он будет нормально компилироваться.
struct A
{
struct B
{
int i = 0;
};
B b;
A(const B& _b = B())
: b(_b)
{}
};
Я тестировал этот код с g ++ версии 4.7.2, 4.8.1, clang ++ 3.2 и 3.3. Помимо того факта, что в этом коде происходит ошибка g ++ 4.7.2 ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770 ), другие протестированные компиляторы выдают сообщения об ошибках, которые мало что объясняют.
g ++ 4.8.1:
test.cpp: In constructor ‘constexpr A::B::B()’:
test.cpp:3:12: error: constructor required before non-static data member for ‘A::B::i’ has been parsed
struct B
^
test.cpp: At global scope:
test.cpp:11:23: note: synthesized method ‘constexpr A::B::B()’ first required here
A(const B& _b = B())
^
clang ++ 3.2 и 3.3:
test.cpp:11:21: error: defaulted default constructor of 'B' cannot be used by non-static data member initializer which appears before end of class definition
A(const B& _b = B())
^
Сделать этот код компилируемым можно и, похоже, это не имеет значения. Есть два варианта:
struct B
{
int i = 0;
B(){} // using B()=default; works only for clang++
};
или же
struct B
{
int i;
B() : i(0) {} // classic c++98 initialization
};
Это действительно неправильный код или компиляторы ошибаются?
c++
c++11
language-lawyer
etam1024
источник
источник
internal compiler error: Segmentation fault
этому коду ...int i = 0
если это не такstatic const int i = 0
.B()
как о вызове функции конструктора. Вы никогда не «вызываете» конструктор напрямую. Подумайте об этом как о специальном синтаксисе, который создает временныйB
... и конструктор вызывается как одна часть этого процесса, глубоко внутри следующего механизма.B
похоже, заставляет эту работу работатьgcc 4.7
.Ответы:
Ну ни то, ни другое. В стандарте есть дефект - он говорит, что и то, что
A
считается завершенным при синтаксическом анализе инициализатора дляB::i
, и то, чтоB::B()
(которое использует инициализатор дляB::i
), может использоваться в определенииA
. Это явно циклично. Учти это:struct A { struct B { int i = (A(), 0); }; A() noexcept(!noexcept(B())); };
В этом есть противоречие:
B::B()
неявно,noexcept
еслиA()
и только если не выбрасывает, иA()
не выбрасывает, если и только неB::B()
является . В этой области есть ряд других циклов и противоречий.noexcept
Это отслеживается по ключевым проблемам 1360 и 1397 . Обратите внимание, в частности, на это примечание в основном выпуске 1397:
Это частный случай правила, которое я реализовал в Clang для решения этой проблемы. Правило Clang заключается в том, что конструктор по умолчанию по умолчанию для класса не может использоваться до того, как будут проанализированы инициализаторы нестатических элементов данных для этого класса. Следовательно, Clang выдает здесь диагностику:
A(const B& _b = B()) ^
... потому что Clang анализирует аргументы по умолчанию до того, как он анализирует инициализаторы по умолчанию, и этот аргумент по умолчанию потребует, чтобы
B
инициализаторы по умолчанию уже были проанализированы (для неявного определенияB::B()
).источник
Может в этом проблема:
Таким образом, конструктор по умолчанию создается при первом поиске, но поиск завершится неудачно, потому что A не полностью определен, и поэтому B внутри A не будет найден.
источник
B b
это не проблема, и найти явные методы / явно объявленный конструктор вB
не проблема. Так что было бы неплохо увидеть какое-то определение того, почему поиск здесь должен происходить по-другому, чтобы «B
внутриA
не было найдено» только в этом одном случае, но не в других, прежде чем мы сможем объявить код незаконным по этой причине.=0
изi = 0;
. Но без этого=0
код действителен, и вы не найдете ни одного компилятора, который жалуется на использование вB()
пределах определенияA
.