7
votes

As a followup to this question, gcc and clang both consider this program ill-formed:

int main() {
    const int& ri = 0;
    constexpr int i = ri;
}

The error is about the value of ri being unusable in a constant expression. 0 is certainly a core constant expression, and as a prvalue core constant expression seems to satisfy these constraints (trivially, since int isn't of class, pointer, or array type). So shouldn't ri satisfy this criteria?

The same is true if I use a prvalue literal of class type:

struct X { };
int main() {
    const X& rx = X{};
    constexpr X x = rx;
}
2
An object can't be of class, pointer and array type simultaneously. No object can satisfy all the constraints. It must satisfy the appropriate one. An object of type int satisfies neither. So no, ri wouldn't apply. - StoryTeller - Unslander Monica
D'oh. Thank you for making me aware that I misread [expr.const]/6. It should read "an object with static storage duration that is (either not a temporary object or is a temporary object whose value satisfies the above constraints)", not "(an object with static storage duration that is either not a temporary object) or (is a temporary object whose value satisfies the above constraints)". - cpplearner
@StoryTeller The constraint P -> Q is satisfied by !P. - Barry
@Barry - I'm reading it more like P1 || P2 || P3. Where it just so happens all three are mutually exclusive. - StoryTeller - Unslander Monica
The reference is initialized with the glvalue result of the temporary materialization conversion applied to the prvalue 0 adjusted to type const int. You can't ignore the conversions and consider only the syntactic form of the initializer. - T.C.

2 Answers

5
votes

In this statement:

const int& ri = 0;

0 is a prvalue, but ri isn't initialized from that prvalue. The prvalue first undergoes a temporary materialization conversion and the reference is bound to the resulting glvalue. Since ri is bound to this materialized glvalue, and not directly to the prvalue like you (I) suspected, the relevant restrictions are not the prvalue core constant expression restrictions (which 0 does satisfy) but rather the glvalue core constant expression restrictions - that the entity be a permitted result of a constant expression. That restriction, spelled with slightly improved clarity, is:

either an object with static storage duration that is:

  • not a temporary object, or
  • a temporary object whose value satisfies the above constraints,

or it is a function.

Our glvalue is a temporary object whose value satisfies "the above constraints" ("above" here referring to the prvalue core constant restrictions, which int trivially satisfies), but it does not have static storage duration.

Not having static storage duration → the entity is not a permitted result of a constant expression → the glvalue expression not a constant expression → ri wasn't initialized with a constant expression → ri can't be used in a core constant expression → the declaration of i is ill-formed.

The same argument holds for appropriate class types as well.

1
votes

As you pointed out, 2.11 states that a core constant expression must not evaluate to:

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

And further expr.const#6:

A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

...

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.

From my reading of this, it means that the RHS of const X& r = (substitute X for some type) must either be an object with static storage duration or a temporary object that meets the above criteria. Since an int does not fit either an object of class type, a pointer type or class/array type, it does not qualify.