[class.mem]/6:
A complete-class context of a class is a
(6.1) function body,
(6.2) default argument,
(6.3) noexcept-specifier ([except.spec]),
(6.4) contract condition, or
(6.5) default member initializerwithin the member-specification of the class. [ Note: A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class. — end note ]
The highlighted text above seems to give support to the following snippet:
#include<iostream>
struct A{
int i = j + 1;
int j = 1;
};
int main(){
A a;
std::cout << a.i << '\n';
std::cout << a.j << '\n';
}
, and I was expecting it to print
2
1
Both GCC and clang print
1
1
but in addition clang gives the following warning:
prog.cc:3:13: warning: field 'j' is uninitialized when used here [-Wuninitialized]
int i = j + 1;
^
prog.cc:8:7: note: in implicit default constructor for 'A' first required here
A a;
^
prog.cc:2:8: note: during field initialization in the implicit default constructor
struct A{
^
1 warning generated.
My assumption is that the code is ill-formed NDR. But why?
Your code has undefined behavior due to [class.base.init]/9
In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
if the entity is a non-static data member that has a default member initializer ([class.mem]) and either
[...] the constructor's class is not a union [...]
the entity is initialized from its default member initializer as specified in [dcl.init];
So, that means
struct A{
int i = j + 1;
int j = 1;
};
is translated to
struct A{
A() : i(j + 1), j(1) {}
int i;
int j;
};
and since i
is initialized first it uses an uninitialized variable and is undefined behavior.
I think that the code is equal to following:
struct A{
int i;
int j;
A():i(j + 1),j(1){}
};
Which shows that the compilers are right. Because the members are initialized in the order (stated somewhere in the standard*) in which they were declared. The in-place declaration initialization should be just syntactic sugar for their initialization in all ctors. So, the code indeed has undefined behaviour because j
is an uninitialized variable.
EDIT: * Found it [10.9.2 Initializing bases and members] (http://eel.is/c++draft/class.base.init)
In a non-delegating constructor, initialization proceeds in the following order:
(13.1) First, and only for the constructor of the most derived class ([intro.object]), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
(13.2) Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
(13.3) Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
(13.4) Finally, the compound-statement of the constructor body is executed.
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