Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

My assumption is that the code below ill-formed NDR? But why?

[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 initializer

within 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?

like image 867
WaldB Avatar asked Mar 05 '19 18:03

WaldB


2 Answers

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.

like image 167
NathanOliver Avatar answered Nov 17 '22 23:11

NathanOliver


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.

like image 36
Quimby Avatar answered Nov 17 '22 22:11

Quimby