13
votes

The expression b in this code shall be a core constant expression

int main()
{
    constexpr int a = 10;
    const int &b = a;
    constexpr int c = b; // here
    return 0;
}

since the standard says (8.20, paragraph 2 [expr.const] in n4700)

An expression e is a core constant expression unless the evaluation of e would evaluate one of the following expressions:

  • ...

  • an lvalue-to-rvalue conversion (7.1) unless it is applied to

    • ...

    • a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable subobject of such an object, or

First, the expression b in the above code is an lvalue (which is also a glvalue) since it's a reference, thereby being a variable (8.1.4.1, paragraph 1 [expr.prim.id.unqual]):

The expression is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise; it is a bit-field if the identifier designates a bit-field (11.5).

Second, the object the variable b denotes is a, and it's declared with constexpr. However, gcc complains

./hello.cpp: In function ‘int main()’:
./hello.cpp:6:20: error: the value of ‘b’ is not usable in a constant expression
  constexpr int c = b;
                    ^
./hello.cpp:5:13: note: ‘b’ was not declared ‘constexpr’
  const int &b = a;

As far as I can tell, a reference is not an object, so the above bullet apparently suggests that a shall be declared with constexpr. Am I missing something? The reason why I don't agree with gcc is that gcc sees b as an object, thereby requiring it to be declared with constexpr. However, b is not an object!

1
Clang agrees with GCC. But I think I agree with you, this is a defect in either the standard or the compilers. Also of note is that MSVC accepts the code.Sebastian Redl

1 Answers

13
votes

One of the rules for core constant expressions is that we can't evaluate:

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
  • its lifetime began within the evaluation of e;

b is an id-expression that refers to a variable of reference type with preceding initialization. However, it is initialized from a. Is a a constant expression? From [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.

a is a glvalue core constant expression (it doesn't hit any of the restrictions in expr.const/2), however it is not an object with static storage duration. Nor is it a function.

Hence, a is not a constant expression. And b, as a result, isn't initialized from a constant expression and so can't be used in a core constant expression. And thus c's initialization is ill-formed as not being a constant expression. Declare a as a static constexpr int, and both gcc and clang accept the program.

C++, you magical beast.