Search code examples
c++language-lawyerthisconstant-expression

How to make `this` pointer constant expression?


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 of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

  • (5.1) this ([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of E;

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.


Solution

  • 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:

    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.


    To answer the title question, this is not a core constant expression, because it is the address of an object with automatic storage duration; that address may change at runtime. This is completely irrelevant for the static_assert in the question code: Being a constant pointer value is neither necessary nor sufficient for a this pointer to be "usable in constant expressions", which in turn is not sufficient for the object found through the pointer to be usable in constant expressions.