The following code is quite trivial and I expected that it should compile fine.
struct A { struct B { int i = 0; }; B b; A(const B& _b = B()) : b(_b) {} };
I've tested this code with g++ version 4.7.2, 4.8.1, clang++ 3.2 and 3.3. Apart from fact that g++ 4.7.2 segfaults on this code (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57770), the other tested compilers give error messages that don't explain much.
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 and 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()) ^
Making this code compilable is possible and seems like it should make no difference. There are two options:
struct B { int i = 0; B(){} // using B()=default; works only for clang++ };
or
struct B { int i; B() : i(0) {} // classic c++98 initialization };
Is this code really incorrect or are the compilers wrong?
Member initialization Non-static data members may be initialized in one of two ways: 1) In the member initializer list of the constructor.
A non-static member function is a function that is declared in a member specification of a class without a static or friend specifier. ( see static member functions and friend declaration for the effect of those keywords)
Non-static data members are the variables that are declared in a member specification of a class. And they have the example: class S { int& r; // non-static data member of reference type };
Static Data Member Initialization in C++ We can put static members (Functions or Variables) in C++ classes. For the static variables, we have to initialize them after defining the class. To initialize we have to use the class name then scope resolution operator, then the variable name. Now we can assign some value.
Is this code really incorrect or are the compilers wrong?
Well, neither. The standard has a defect -- it says both that A
is considered complete while parsing the initializer for B::i
, and that B::B()
(which uses the initializer for B::i
) can be used within the definition of A
. That's clearly cyclic. Consider this:
struct A { struct B { int i = (A(), 0); }; A() noexcept(!noexcept(B())); };
This has a contradiction: B::B()
is implicitly noexcept
iff A()
does not throw, and A()
does not throw iff B::B()
is not noexcept
. There are a number of other cycles and contradictions in this area.
This is tracked by core issues 1360 and 1397. Note in particular this note in core issue 1397:
Perhaps the best way of addressing this would be to make it ill-formed for a non-static data member initializer to use a defaulted constructor of its class.
That's a special case of the rule that I implemented in Clang to resolve this issue. Clang's rule is that a defaulted default constructor for a class cannot be used before the non-static data member initializers for that class are parsed. Hence Clang issues a diagnostic here:
A(const B& _b = B()) ^
... because Clang parses default arguments before it parses default initializers, and this default argument would require B
's default initializers to have already been parsed (in order to implicitly define B::B()
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With