This is a follow-up question is my previous question: Why are member functions returning non-static data members not core constant expressions?
The reduced version of the example mentioned in that question is:
struct S {
const bool x = true;
constexpr bool f() { return x; }
};
int main() {
S s{};
static_assert(s.f()); // error: 's' is not a constexpr;
}
The applicable wording from the standard is N4861: [expr.const]/(5.1):
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.1)
this
([expr.prim.this]), except in aconstexpr
function ([dcl.constexpr]) that is being evaluated as part ofE
;
As far as I can parse, the expression E
is s.f()
and it evaluates this
since s.f()
returns a non-static member this->x
. But that falls under the "except" part: the member function s.S::f()
is constexpr function that's being evaluated as part of s.f()
. If I parsed correctly, I'm expecting s.f()
to be constant expression and the assertion success.
However, this bullet doesn't specify a requirement that says that s
has to be a constant expression. I can't understand why declaring s
as constexpr
compiles the program even though there's no requirement, defined in this bullet, for s
to be constexpr
.
I'm just applying the wording (5.1)
in my example but I can't see that constexpr
is required here unless I'm missing any other rule.
CodePudding user response:
Because return x;
performs lvalue-to-rvalue conversion, the whole kaboodle is not a core constant expression:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
an lvalue-to-rvalue conversion unless it is applied to
a non-volatile glvalue that refers to an object that is usable in constant expressions, or
a non-volatile glvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of E;
lvalue-to-rvalue conversion is applied to this->S::x
, which is generally forbidden, and neither of the exceptions apply to permit it.
The more relevant exception applies if x
(which resolves to this->S::x
) is an object that is usable in constant expressions. But it only would be if the struct S
object were usable in constant expressions:
That requires it to be potentially-constant:
And S s{};
is not potentially-constant. So it is not usable in constant expressions, and neither are its subobjects.