I have the following code that demonstrate my problem:
int main(void)
{
const int ci = 42;
constexpr int j = ci;
}
The above program compiles fines. But I'm expecting it to be ill-formed.
First, the initializer 42
is an integral constant expression converted to int
via identity conversion; then, the converted expression is a core constant expression: ([const.expr]/9)
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression.
Second, I claim that the expression E = ci
is not usable in constant expression because it's not constant-initialized (i.e, it's variable has no static duration) even though the expression ci
is potentially-constant variable (i.e, it's variable of const-qualified integral type). So you can't apply the rule [expr.const]/4 because it requires the object to be potentially-constant as well as constant-initialized:
A constant-initialized potentially-constant variable V is usable in constant expressions at a point P if V's initializing declaration D is reachable from P and ..
So per my understanding, for variable ci
to be constant-initialized, it has to have a static duration as well as a constant-expression initializer.
Assuming my understanding is correct so far, I will continue.
In the initialization of j
, an lvalue-to-rvalue conversion is performed on the glvalue ci
; but this conversion is applied to a non-volatile glvalue that refers to an object ci
that is not usable in constant expressions. So I'm expecting the program to be ill-formed because the expression E
evaluates an lvalue-to-rvalue conversion and neither rule in [expr.const]/(5.9) permits it. Am I correct? What I'm missing/conflating here? Am I missing any wording?
As a sidenote, in C++17, the rule regarding this point is more restricted and clear at least for me:
an lvalue-to-rvalue conversion unless it is applied to
- (2.7.1) a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression [..]
It's definitely clear to me that the wording is applied here and (2.7.1)
is satisfied. But the wording regarding this point specifically is changed in C++20: The term usable "usable in constant expressions" appears since C++20 per my search. Note that, if possible, I need a C++20 answer.
By the definition of "constant-initialized" ([expr.const]/2), the following is constant-initialized because 42
is a constant expression.
int ci = 42;
By the definition of "potentially-constant" ([expr.const]/3), the following is potentially-constant because the variable is a const-qualified integral type.
const int ci;
If you have both of these at function scope, then you have a variable that is usable in constant expressions from the point of definition until the end of the variable's scope (special case of [expr.const]/4).