[basic.life]/2 says that
The lifetime of an object of type T begins when:
- storage with the proper alignment and size for type T is obtained, and
- its initialization (if any) is complete (including vacuous initialization) ([dcl.init]),
except that ...
And the initialization of the non-static data member is not performed before the base class's constructor returns, because [class.base.init]/13 says
In a non-delegating constructor, initialization proceeds in the following order:
- 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.
- 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).
- 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).
- Finally, the compound-statement of the constructor body is executed.
So the lifetime of the data member value hasn't started yet when the write to it is executed in the constructor of Base.
[basic.life]/8 says that
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.allocation]), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if
- (8.1) the glvalue is used to access the object, or
...
Therefore the write to static_cast<D*>(this)->value has undefined behavior, which is not fine. Moreover, since it is evaluated at compile-time, and according to [expr.const.core]/2:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
- ...
- (2.8) an operation that would have undefined or erroneous behavior as specified in [intro] through [cpp];
- ...
the program is ill-formed since the compile-time evaluation involves a core language undefined behavior.
Clang is correct to diagnose this and reject the program.
Cameron Singh
· 0 rep
· 16 hours ago