Is this behavior undefined or a false positive warning?

Consider the following code:

class A { private: int a; public: A(int a) : a(a) { } }; class B : public A { private: int b; bool init() { b = 0; return true; } public: // init() is a hack to initialize b before A() // B() : b(0), A(b) {} yields -Wreorder // B() : A((b = 0)) {} no warning (but this one doesn't work so well with non-pod (pointer) types) B() : A(init() ? b : 0) {} }; 

Now, trying to compile this code with clang ...

 $ clang++ test.cpp -fsyntax-only test.cpp:19:20: warning: field 'b' is uninitialized when used here [-Wuninitialized] B() : A(init() ? b : 0) {} ^ 1 warning generated. 

GCC does not print any warnings, even with -Wall -Wextra and -pedantic .

+8
c ++ undefined-behavior constructor clang ++
source share
2 answers

This behavior is undefined. According to [class.base.init]:

In the constructor without delegation, initialization is performed in the following order:
- Firstly, and only for the constructor of the most derived class (1.8), virtual base classes ...
- Then direct base classes are initialized in the order of declaration, since they are displayed in the list-specifier-base (regardless of the order of mem-initializers).
- Then non-static data elements are initialized in the order in which they were declared in the class definition (again, regardless of the order of mem-initializers).

b will not be initialized by the time the base class A is initialized. Assigning b = 0 itself undefined for the same reason - b has not yet been initialized when it is called. Its default constructor will still be called after constructor A

If you want to make sure b initialized first, a typical approach is the base-and-member idiom :

 struct B_member { int b; B_member() : b(0) { } }; class B : public B_member, public A { public: B() : A(b) // B_member gets initialized first, which initializes b // then A gets initialized using 'b'. No UB here. { }; }; 
+7
source share

In any case, calling a member function before initializing the base classes causes undefined behavior. ยง12.6.2 / 16:

Member functions (including virtual member functions, 10.3) may be called upon to construct the facility. Similarly, an object, the construction can be an operand of the typeid operator (5.2.8) or dynamic_cast (5.2.7). However, if these operations are performed in the ctor-initializer (or in a function called directly or indirectly from the ctor-initializer) before all mem-initializers for the base classes are completed, the result of the operation is undefined. [Example:

 class A { public: A(int); }; class B : public A { int j; public: int f(); B() : A(f()), // undefined: calls member function // but base A not yet initialized j(f()) { } // well-defined: bases are all initialized }; 

However, the access and assignment to b itself is wonderful, since it has an empty initialization, and its lifetime begins as soon as the memory is acquired for it (which happened long before the start of the constructor call). Consequently,

 class B : public A { private: int b; public: B() : A(b=0) {} }; 

clearly defined.

+6
source share

All Articles