Is GCC correct in requiring the constexpr specifie

2019-04-25 00:55发布

问题:

The code below doesn't compile under GCC 5.3.0 because the declaration of r is missing a constexpr specifier.

const int i = 1;
const int& r = i;
constexpr int j = r;

I believe the rejection is correct. How do I prove it using the working draft N4527?

回答1:

First, since we're using a reference, [expr.const]/(2.9) must not be violated. (2.9.1) applies, though:

an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
— it is initialized with a constant expression

I.e. using r is fine, as long as the initializer - i - is a constant expression (this is shown below).
It's also necessary to check whether the l-t-r conversion in line 3 is legal, i.e. (2.7) must not be violated. However, (2.7.1) applies:

an lvalue-to-rvalue conversion (4.1) unless it is applied to
— a non-volatile glvalue of integral or enumeration type that refers to a complete non-volatile const object with a preceding initialization, initialized with a constant expression, or

…so that's fine as well, since the (g)lvalue is r, and it refers to i - which is a non-volatile const object with a constant expression initializer (1).

We postponed showing that i is actually a constant expression, and once that's out of the way, we need to show that r is a constant expression.
[expr.const]/5 pertains to that:

A constant expression is either a glvalue core constant expression whose value refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value is an object where, for that object and its subobjects:

  • each non-static data member of reference type refers to an entity that is a permitted result of a constant expression, and
  • if the object or subobject is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

Since i is, in the above context, a (g)lvalue, it has to be a permitted result of a constant expression - which it is, since it has static storage duration and certainly isn't a temporary. Thus i is a constant expression.

r is, however, treated as a prvalue in line 3. Since we already established that r is a core constant expression, we solely need to check the bullet points. They're clearly met, though.

Hence the code is well-formed in namespace scope. It won't be in local scope, as i wouldn't be a permitted result of a constant expression anymore. Clang gives a comprehensive error message.