Что такое constexpr в С++?

Я действительно запутался в концепции constexpr, так как я прочитал constexpr, оценивается во время компиляции, поэтому он полезен для оптимизации производительности по сравнению с обычным const.

constexpr int i = 0;
constexpr int& ri = i;

Приведенный выше код возвращает ошибку "неверная инициализация ссылки типа" int & "из выражения типа" const int", почему?

Кроме того, следующий код имеет ошибку:

constexpr int i = 0;
constexpr int* ri = &i;

Если я заменил ключевое слово constexpr на const, все вышеописанное работало правильно.

Ответы

Ответ 1

Re

", если я заменил constexpr-слово на const, все вышеописанное выполнено правильно.

A const int* по существу означает (const int)*, за исключением того, что вы не можете использовать круглые скобки таким образом. A constexpr int* означает constepxr (int*) (примечание).

Это связано с тем, что constexpr не является частью этого типа, вы не можете назвать тип constexpr int, скажем, в то время как const является частью этого типа.

Вместо

constexpr int i = 0;
constexpr int& ri = i;

который пытается объявить ссылку constexpr для не const, просто напишите

constexpr int i = 0;
constexpr int const& ri = i;

Вы можете прочитать это, так как ri является ссылкой на const int, который равен constexpr (оценивается во время компиляции).


Добавление

Это означает, что для С++ 14 для локальных объектов < <2 → constexpr требуется продолжительность автоматического хранения, по модулю правила as-if для оптимизации.

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

void oops()
{
    static constexpr int i = 0;      // Necessary with some compilers.
    constexpr int const& ri = i;
}

В противном случае он может не компилироваться, например. g++, и это, вероятно, то, что требуют стандарты С++ 14 и С++ 11, путем исключения подходящих ограничений на constexpr.

Примечания:
¹ См. Обсуждение ответа Р. Саху.

Ответ 2

constexpr int i = 0;
constexpr int  * ri = &i;

Вторая строка - проблема, потому что указатель не указывает на объект const. Сам указатель const.

Использование

constexpr int i = 0;
constexpr int const * ri = &i;

решает эту проблему. Однако это будет проблемой, если переменные определены в области функций.

constexpr int i = 0;
constexpr int const* ri = &i;

int main() {}

- действительная программа.

void foo()
{
   constexpr int i = 0;
   constexpr int const* ri = &i;
}

int main() {}

не является допустимой программой.

Вот что должен сказать стандарт С++ 11 о постоянном выражении адреса:

5.19 Константные выражения

3.. Выражение константы адреса представляет собой выражение константы основного значения prvalue типа указателя, которое оценивает адрес объекта со статической продолжительностью хранения, адресом функции или значением нулевого указателя или константным постоянным выражением типа prteue типа std::nullptr_t.

Ответ 3

Как вы сказали, constexpr оценивается в время компиляции. Поэтому при компиляции значение должно оцениваться.

Например:

constexpr int i = 0;
constexpr int& ri = i;

Для первой строки значение 0 оценивается при компиляции, его значение равно 0.

Но для второй строки компилятор нуждается в адресе i для выполнения задания, которое определяется во время выполнения. Таким образом, эта строка не будет выполнена.

Ответ 4

Вот мои 2 цента:

Функция constexpr определяет вычисление, которое происходит во время компиляции. Разумный вопрос:

  • Имеет ли смысл разрешать любое вычисление?

Разумный ответ:

  • Нет, потому что это потребует большого количества машин, ресурсов и т.д. прямо внутри компилятора, в то время как нет никакой прямой необходимости в этом.

Из-за этого стандарта допускается довольно ограниченный набор функций внутри кода constexpr. Можно возразить, почему именно этот набор, а не что-то еще? Ну, позже стандарт может развиться и позволить больше.