3
votes
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?

2
constexpr int value = func(value); is constexpr int value = func(data);nop666
@nop666 yes, a typo. I have modified it.xmh0511
uh.. a local reference's initializer based on local variable is never a a constant expression afaik, because of rfs life span <.<. And technically rf had not been initialized as far as that scope is concerned. Both rf and v are local variables. If you initialize rf, there will be no error, for that it should not be a function parameter.Swift - Friday Pie
@Swift-FridayPie So why in the definition of func in the first example, rf does not violate the bullet [expr.const#2.11]?xmh0511
My understanding is that it is what called "decay" in linguistics. If first case expression rf doesn't refer to a reference type contextually but to related lvalue, because return value matches life span of rf. Attempting to use it as constexpr reference by initializing a reference fails because of 2.11. And mpark said, the key is visibility and life span. constexpr for v extended lifespan of v to point before function would be used.Swift - Friday Pie

2 Answers

4
votes

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

2
votes

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.