Почему это выражение не является постоянным выражением?

Выражение b в этом коде должно быть выражением постоянной константы

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

так как стандарт говорит (8.20, параграф 2 [expr.const] в n4700)

Выражение e является выражением постоянной константы, если оценка e будет оценивать одно из следующих выражений:

  • ...

  • преобразование lvalue-to-rvalue (7.1), если оно не применяется к

    • ...

    • неизменяемый glvalue, который ссылается на энергонезависимый объект, определенный с помощью constexpr, или который ссылается на не изменяемый подобъект такого объекта, или

Во-первых, выражение b в приведенном выше коде является lvalue (которое также является glvalue), поскольку оно является ссылкой, тем самым являясь переменной (8.1.4.1, абзац 1 [Expr.prim.id.unqual]):

Выражение представляет собой lvalue, если объект является функцией, переменная, или элемент данных и значение prvalue иначе; это бит-поле, если идентификатор обозначает бит-поле (11.5).

Во-вторых, объект, обозначаемый переменной b, является a, и он объявлен с помощью constexpr. Однако gcc жалуется

./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;

Насколько я могу судить, ссылка не является объектом,, поэтому вышеуказанная марка, по-видимому, предполагает, что a объявляется с помощью constexpr. Я что-то упускаю? Причина, по которой я не согласен с gcc, состоит в том, что gcc видит b как объект, тем самым требуя, чтобы он был объявлен с помощью constexpr. Однако b не является объектом!

Ответ 1

Одно из правил для основных константных выражений состоит в том, что мы не можем оценить:

id-выражение, которое ссылается на переменную или элемент данных ссылочного типа, если ссылка не имеет предыдущей инициализации и либо

  • он инициализируется константным выражением или
  • его время жизни начиналось с оценки e;

b - это id-выражение, которое ссылается на переменную ссылочного типа с предшествующей инициализацией. Однако он инициализируется из a. Является ли a постоянным выражением? Из [expr.const]/6:

Постоянное выражение представляет собой либо выражение константы ядра glvalue, которое относится к объекту, который является допустимым результатом константного выражения (как определено ниже), или константным выражением основного значения prvalue, значение которого удовлетворяет следующим ограничениям: [... ]

Сущность является допустимым результатом постоянного выражения, если это объект со статической продолжительностью хранения, который либо не является временным объектом, либо является временным объектом, значение которого удовлетворяет указанным выше ограничениям, либо оно является функцией.

a - выражение константы ядра glvalue (оно не затрагивает никаких ограничений в expr.const/2), однако это не объект со статической продолжительностью хранения. И это не функция.

Следовательно, a не является постоянным выражением. И b, как результат, не инициализируется из константного выражения и поэтому не может использоваться в основном постоянном выражении. И, таким образом, инициализация c плохо сформирована, поскольку она не является постоянным выражением. Объявите a как static constexpr int, и gcc и clang принимают программу.

С++, вы волшебный зверь.