Question Details

No question body available.

Tags

c++ clang language-lawyer crtp

Answers (2)

July 14, 2026 Score: 9 Rep: 105,648 Quality: Medium Completeness: 70%

I assume the problem is we write to the member of the derived class inside the base class constructor, which means we are writing data in an object that hasn't been constructed yet

Correct.

which is apparently fine in runtime initialization, but is not fine in a constexpr context.

It's never fine, the behavior is undefined. You happened to get away with it.


[basic.life]/7.2

Before the lifetime of an object has started ... The program has undefined behavior if ...

— the pointer is used to access a non-static data member or call a non-static member function of the object, ...

This makes no exceptions for non-constexpr context.

July 14, 2026 Score: 4 Rep: 1,079 Quality: Medium Completeness: 100%

[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(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.