c++staticconstant-expression

Why initializing a 'const reference to base' object from 'constexpr derived' object doesn't yields constant expression?


I have this simple example:

struct base
{
   constexpr base () {};
};
   
struct derived: public base
{
    constexpr derived () {};
};
  

int main() 
{
    constexpr derived d{};
    constexpr const base &b = d;  // why error?
}

G++ gives me the following error:

error: '(const base&)(& d)' is not a constant expression

And clang++ gives me following error:

error: constexpr variable 'b' must be initialized by a constant expression.

note: reference to subobject of 'd' is not a constant expression. Add 'static' to give it a constant address.

It's clear to me that clang gives the solution but I cannot understand why the problem is solved?

It said that "the reference to subobject of d is not a constant expression.", and the subobject here has constexpr constructor so why it's not constant expression?

Finally, I don't understand why using static solves the problem?

Please includes the answer with a quote from the standard (if possible).


Solution

  • The explanation given by Clang is correct.

    Constexpr doesn't change the storage of a variable (where it's put in memory). If you declare a non-static constexpr variable, it'll still go onto the stack, just like all other non-static local variables do.

    int test() {
      constexpr int a = 3; //a gets created here and put on the stack
      const int *b = &a; //Pointer to a location on the stack
    } //a gets destroyed when the function ends
    

    This means that the address of that local variable can change with every invocation of the function. There can also be multiple copies of the variable at any given time, i.e. if multiple threads execute the function or if the function recurses.

    The overall net effect is that the address of a local variable can never be a constant expression, no matter if that variable is const, constexpr, volatile, or anything else.

    The static keyword changes this. It "promotes" the local variable into static storage, meaning that it behaves like a global variable that is only visible in that function. In particular, the variable has a fixed address and there is only one copy of the variable, no matter how often (if at all) the function gets executed. The address of a global variable is a constant expression.

    If you want a single, global, constant instance of derived d, just add static in front of it like Clang suggests.