С++: почему это constexpr не является постоянной времени компиляции

В следующем коде С++ 11 последний вызов arraySize вызывает ошибку компиляции. По-видимому, это потому, что y является массивом размера времени выполнения, а параметр шаблона N массива N не может быть выведен для y. Я не понимаю, почему x является массивом размера времени компиляции, но y заканчивается размером времени выполнения. Функция шаблона arraySize берется непосредственно из пункта 1 "Эффективный современный С++" Скотта Мейерса.

#include <cstddef>

template<typename T, std::size_t N>
constexpr std::size_t arraySize(T(&)[N]) noexcept { return N; }

struct S
{
    char c[10];
};

int main()
{
    S s;
    S* ps = &s;

    char x[arraySize(s.c)];
    char y[arraySize(ps->c)]; // why is y a runtime sized array?

    arraySize(x);
    arraySize(y); // error !?

    return 0;
}

Ответ 1

В С++ ошибка не является вызовом arraySize(y), а объявлением самой y.

Оценки в объявлении массива должны быть "преобразованным постоянным выражением".

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

Значительное различие между arraySize(s.c) и arraySize(ps->c) заключается в том, что ps->c совпадает с (*ps).c, а оператор разыменования * требует преобразования lvalue-to-r на ps, который не является константой выражение (равно &s, см. ниже). Остальная часть выражения не включает преобразование lvalue-в-rvalue, массив lvalue напрямую связан ссылкой.

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

  • каждый нестатический элемент данных ссылочного типа относится к объекту, который является допустимым результатом константы выражение и

  • , если объект или подобъект имеет тип указателя, он содержит адрес объекта со статической продолжительностью хранения, адрес после конца такого объекта (5.7), адрес функции, или значение нулевого указателя.

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

Ясно, что ps содержит адрес объекта с автоматическим временем хранения, поэтому его нельзя объявить constexpr. Но все должно начать работать, если вы измените S s; S* ps = &s; на static S s; constexpr S* ps = &s;

(С другой стороны, вы думаете, что параметр arraySize(s.c) также не является константным выражением, поскольку он является ссылкой, а не объектом статической продолжительности хранения)