c++language-lawyerc++20constant-expression

objects that're usable constant expressions


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.


Solution

  • 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).