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?
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)
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With