This code is ill-formed, and GCC 8 and 9 are incorrect to not give a diagnostic.
[expr.const] (C++17 paragraph 2, current C++20 draft paragraph 4):
An expression e
is a core constant expression unless the evaluation of e
, following the rules of the abstract machine, would evaluate one of the following expressions:
...
an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard;
...
modification of an object unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e
;
...
"Clauses [intro] through [cpp]" is also known as the core language specification.
[expr.const] (C++17 paragraph 5, current C++20 draft paragraph 10):
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:
The "following constraints" apply only to values of class type, array type, or pointer type.
[dcl.constexpr] (C++17 paragraph 9, current C++20 draft paragraph 10):
In any constexpr
variable declaration, the full-expression of the initialization shall be a constant expression.
The expression --const_cast<int&>(a);
is not a core constant expression and therefore not a constant expression, both because its evaluation would have undefined behavior, and because it modifies an object whose lifetime did not begin within the evaluation. A "shall" statement (unless combined with "no diagnostic required") means that an implementation must print a diagnostic message (e.g. an error or warning) when a program violates it.