Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is the meaning of the phrase "preceding initialization" in section [expr.const]

constexpr int func(int const& rf){
    return rf;
}
int main(){
   constexpr int value = func(0);
}

Consider the above code, the variable value shall be initialized by a constant expression, which is func(0), which firstly shall be a core constant expression. To determine whether the expression func(0)is a core constant expression, the following rules will be applied to it, that is:
expr.const#2.7

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 lvalue-to-rvalue conversion unless it is applied to

[...], or

(2.7.4) a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

Despite the lvalue-to-rvalue conversion is applied to rf and such conversion satisfied the bullet (2.7.4), however, take a look to the next paragraph, that is:
expr.const#2.11

an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either

  • (2.11.1) it is initialized with a constant expression or,
  • (2.11.2) its lifetime began within the evaluation of e;

I don't know what actually the phrase preceding initialization mean? Does it mean that a variable should be initialized before using it, or it means that in a declaration of a variable shall have an initializer. Anyhow, before applying the lvalue-to-rvalue conversion to glvalue rf, the glvalue rf should be evaluated to determine the identity of an object, which is ruling by:

A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.

That means not only bullet [expr.const#2.7] shall be obeyed but also [expr.const#2.11] shall be obeyed.

Because id-expression rf is of reference type. So, in order to make the expression func(0) be a core constant expression, the id-expression rf must have a preceding initialization, and satisfies at least one of bullet (2.11.1) and (2.11.2). In my example, the bullet (2.11.2) is obeyed by rf and the compiler agree that func(0) is a constant expression. The outcome is here, the outcome seems to evidence that preceding initialization means be initialized rather than have an initializer due to the parameter declaration does not have an initializer in the first example.

In order to check such a thought, I test the below code:

constexpr int func(int const& rf){
    constexpr int const& v = rf;  
    return 0;
}
int main(){
   static int const data = 0;
   constexpr int value = func(data);
}

The outcomes of the compilers point out rf is not a constant expression. I'm confuse at this outcome. According to the above supposition. rf has a preceding initialization, and the bullet (2.11.1) is obeyed because data is a constant expression, even if the bullet (2.11.2) does not be satisfied.

So, I wonder what actually does the phrase preceding initialization mean? If it means that a declaration for a variable has an initializer, how could the expression func(0) in the first example be a constant expression?

like image 518
xmh0511 Avatar asked Mar 02 '23 03:03

xmh0511


2 Answers

The outcome seems to evidence that "preceding initialization" means "be initialized" rather than have an initializer due to the parameter declaration does not have an initializer in the first example.

It does mean "be initialized", but it's more importantly about the visibility of a preceding initialization within the context of the expression being evaluated. In your example, in the context of evaluating func(0) the compiler has the context to see the initialization of rf with 0. However, in the context of evaluating just the expression rf within func, it doesn't see an initialization of rf. The analysis is local, as in it doesn't analyze every call-site. This leads to expression rf itself within func not being a constant expression while func(0) is a constant expression.

If you were to write this instead:

constexpr int func(int const& rf) {
  /* constexpr */ int const& v = rf;
  return v;
}

int main() {
  static int const data = 0;
  constexpr int value = func(data);
}

This is okay again because in the context of func(data), rf has a preceding initialization with a constant expression data, and v has a preceding initialization with rf which is not a constant expression but its lifetime began within the evaluation of func(data).

like image 146
mpark Avatar answered Mar 04 '23 17:03

mpark


This is CWG2186:

2186. Unclear point that “preceding initialization” must precede

Similar to the concern of issue 2166, the requirement of 8.20 [expr.const] bullet 2.7.1 for

— 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, or

does not specify the point at which the determination of “preceding initialization” is made: is it at the point at which the reference to the variable appears lexically, or is it the point at which the outermost constant evaluation occurs? There is implementation divergence on this point.

But the meaning should be "lexically", because ODR.

like image 32
Language Lawyer Avatar answered Mar 04 '23 16:03

Language Lawyer