Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this using another constant variable defined later qualify an expression as a non constant expression

Considering the example from this page, reproduced below

struct S {
    static const int c;
};
const int d = 10 * S::c; // not a constant expression: S::c has no preceding
                         // initializer, this initialization happens after const
const int S::c = 5;      // constant initialization, guaranteed to happen first

Why is the initialization of d not a constant expression (and therefore not a part of the constant initialization process)? The comment seems to say that it is because it uses a value that does not have a preceding initializer, but that does not seem to be mentioned in the list of conditions that qualify an expression to be a constant expression (The conditions are listed here). In particular which condition that qualifies something to be a constant expression does it violate?

If it has something to do with the fact that constant intialization has to be evaluated at compile time, then the standard mentions that constant initialization does not need to happen at compile time, and can even happen at say for example load time. Then why not just initialize c at compile time and just d at load time? (I might be thinking myself into a circle here)


Thanks to Jayesh I was able to find a similar question "surprising" constant initialization because of definition order but the answer seems to talk about an lvalue to rvalue conversion, where is the lvalue to rvalue conversion here? Other than that there was no quote from the standard there about which condition was being violated here. The answers also don't explain why the initialization is not split up into being at load time and at compile time.

like image 600
Curious Avatar asked Jun 30 '17 06:06

Curious


1 Answers

According to the standard §6.6.2/p2 Static initialization [basic.start.static]:

Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer for the entity.

Now, at the moment of d's initialization the compiler sees that c is not statically initialized yet (i.e., not defined yet). Therefore, the initializer of d doesn't qualify as a constant expression and consequently the initialization of d qualifies as a dynamic initialization. Because static initialization happens before dynamic initialization, c is going to be initialized before d and therefore at the time of d's. initialization c is already initialized and therefore the code qualifies as valid.

Now, if you change the order of initialization or you initialize c inline, the initializer of d qualifies as a constant expression because the compiler at the moment of d's initialization has already seen the static initialization of c (i.e., the definition of c).

Now, the answer as to why in the first case the initializer of d is not a constant expression is given by §8.20/p2 Constant expressions [expr.const] (emphasis mine):

2 An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (4.6), would evaluate one of the following expressions:

...

2.7 — an lvalue-to-rvalue conversion (7.1) 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.

In the expression 10 * c, c has no preceding initialization therefore is not a constant expression.

like image 110
101010 Avatar answered Oct 13 '22 00:10

101010