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?
constexpr int value = func(value);
isconstexpr int value = func(data);
– nop666rf
s life span <.<. And technicallyrf
had not been initialized as far as that scope is concerned. Bothrf
andv
are local variables. If you initializerf
, there will be no error, for that it should not be a function parameter. – Swift - Friday Piefunc
in the first example,rf
does not violate the bullet [expr.const#2.11]? – xmh0511rf
doesn't refer to a reference type contextually but to related lvalue, because return value matches life span ofrf
. 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
forv
extended lifespan ofv
to point before function would be used. – Swift - Friday Pie