19

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!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 2
    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 Dec 20 '17 at 15:36

1 Answers1

18

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 3
    `b` is initialized with a glvalue, and **`a` as a glvalue** is not a constant expression. – cpplearner Dec 20 '17 at 15:42
  • indeed, note that clang refuses to compile also "constexpr int const& b = a;" saying "reference to 'a' is not a constant expression"... – Massimiliano Janes Dec 20 '17 at 15:46
  • Maybe because `a` is a local variable, so the underlying pointer would change? – Sebastian Redl Dec 20 '17 at 15:47
  • Would you guys pinpoint the specific bullet hindering `a` from being a constant expression? I don't think that there is one... – eca2ed291a2f572f66f4a5fcf57511 Dec 20 '17 at 15:49
  • 1
    [\[expr.const\]/6](http://eel.is/c++draft/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[...]. 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." – cpplearner Dec 20 '17 at 15:50
  • ... `a` is not "an object with static storage duration" or "a temporary object" or "a function". – cpplearner Dec 20 '17 at 15:51
  • @SebastianRedl, yes, and the same reasoning seems to allow concluding that b is *not* initialized with a constant expression ... hence neutralizing Barry's quote ? – Massimiliano Janes Dec 20 '17 at 15:54
  • Can we suggest that a glvalue expression can also be considered as a prvalue expression, since the conversion is possible? – eca2ed291a2f572f66f4a5fcf57511 Dec 20 '17 at 15:58
  • With the modification that you mentioned, clang 3.8.0 indeed accepts it... It's weird that gcc still rejects it. Which version did you use? – eca2ed291a2f572f66f4a5fcf57511 Dec 20 '17 at 16:12
  • Well, saying that "`a` is not a constant expression" would be incorrect. `a` is a constant expression in prvalue contexts. You can use `a` to declare bit-field width, for example. But apparently the original comment made by @cpplearner is the right way to see it: it is not a constant expression *in glvalue contexts* specifically. – AnT stands with Russia Dec 20 '17 at 16:13
  • 3
    @AnT There, I disagree. Bit-field asks for an "integral constant expression" which is not a subset of "constant expression" but rather "an expression of integral [...] type, implicitly converted to a prvalue, where the converted expression is a core constant expression." Different requirement. – Barry Dec 20 '17 at 16:20