As a follow-up to this question, clang accepts the code provided there. This question has the following code:
constexpr int func(int const& rf){ return rf; } int main(){ constexpr int value = func(0); }
This question has a good answer, but it follows the C++17 standard. As far as I can tell, the wording regarding constant expression rules is relatively changed from C++17 to C++20 and later.
Basically, It has to determine whether the call expression func(0)
is a constant expression; so firstly we have to know whether the call expression is core constant expression or not, which is governed by the rules defined in [expr.const]/5:
An expression
E
is a core constant expression unless the evaluation ofE
, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
- (5.8) an lvalue-to-rvalue conversion unless it is applied to
- (5.8.1) a non-volatile glvalue that refers to an object that is usable in constant expressions, or
- (5.8.2) a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of
E
;
The rule (5.8)
is applied because the expression E
evaluates an lvalue-to-rvalue conversion: that's, the lvalue rf
has to be converted to prvalue as the returned value of the function call.
According to (5.8.1)
, the expression rf
is a non-volatile glvalue; but, does it is usable in constant expressions? Per [expr.const]/4:
- [..]
An object or reference is usable in constant expressions if it is
- (4.4) a variable that is usable in constant expressions, or
- [..]
- (4.7) a temporary object of non-volatile const-qualified literal type whose lifetime is extended ([class.temporary]) to that of a variable that is usable in constant expressions, or
I'm not so sure whether bullet (4.7)
is applicable to this case. But I think that it might be applicable because note that rf
is bound to a materialized temporary of non-volatile const-qualified literal type whose lifetime is extended and that temporary is usable in constant expression because it's potentially-constant as well as constant-initialized.
Also, I can't find any reason why (4.4)
is not applicable: rf
is a variable and it's usable in constant expressions because it's potentially-constant and constant-initialized with the value 0
.
So that's my confusion: which bullet (if any) is applicable here? and why?
If neither of (4.4)
or (4.7)
is applicable to this case, that means rf
may not be usable in constant expression which also means that (5.8.1)
is not satisfied and (5.8.2)
is then tried. I'm not having any problem with (5.8.2)
: if (5.8.1)
failed, (5.8.2)
succeeded because the lifetime of rf
exactly began within the evaluation of func(0)
. If that's the case, why (5.8.1)
is not satisfied? My confusion specifically is why (4.7)
is not satisfied.
Also note that [expr.const]/(5.12) is not reached: (5.8)
is tried first.