Почему эти фрагменты обрабатываются по-разному GCC?

Первый фрагмент компиляции без каких-либо предупреждений (живой пример):

#include <iostream>

struct A {
  constexpr A(): i(5){}
  constexpr operator int() { return 5; }
  int i;
};

int main() {
    A a;
    int b[a]{ 0, 1, 2, 3, 4 };
    std::cout << b[4] << '\n';
}

Теперь измените приведенный выше фрагмент, вернув i в оператор преобразования (живой пример):

constexpr operator int() { return i; }

GCC предупреждает, что b является VLA.

Для меня оба варианта, похоже, соответствуют пункту §5.19 [expr.const]/3 в С++ 14.

Ответы

Ответ 1

Вы выполняете преобразование l-t-r на i, но для [expr.const]/(2.7) здесь не должно быть нарушено (2.7.3):

введите описание изображения здесь

(2.7.1) касается полных объектов, (2.7.2) говорит о строковых литералах и (2.7.4) относится к объектам, чье жизненное время начиналось с оценки выражения - неприменимо, поскольку объявление a предшествует b 's.

Определите a как constexpr, и код совместим.


Небольшое добавление, чтобы уточнить, что говорит стандарт: выражение внутри скобок должно быть преобразованным константным выражением типа std::size_t ([dcl.array ]/1), который определен в [expr.const]/4 как

Преобразованное константное выражение типа T является выражением, неявно преобразованным в тип T, где преобразованный выражение - это постоянное выражение и [... требования, которые удовлетворяются...]

Таким образом, действительно, стандарт интересует, есть ли или нет

constexpr std::size_t s = a; 

будет действительным. Что это не так по указанным выше причинам - попытка использовать подобъект ранее определенного объекта не constexpr.

Ответ 2

Размер массива должен быть константами времени компиляции, но во втором примере инициализация A::i выполняется не до выполнения.