Constexpr vs. static const: какой из них предпочтительнее?
Для определения констант времени компиляции интегральных типов, таких как следующие (в области функций и классов), какой синтаксис лучше всего?
static const int kMagic = 64; // (1)
constexpr int kMagic = 64; // (2)
(1)
работает также для компиляторов С++ 98/03, вместо этого (2)
требуется не менее С++ 11. Есть ли другие различия между ними? Должен ли тот или иной быть предпочтительным в современном коде на С++ и почему?
ИЗМЕНИТЬ
Я попробовал этот пример кода с Godbolt CE:
int main()
{
#define USE_STATIC_CONST
#ifdef USE_STATIC_CONST
static const int kOk = 0;
static const int kError = 1;
#else
constexpr int kOk = 0;
constexpr int kError = 1;
#endif
return kOk;
}
а для случая static const
это сгенерированная сборка по GCC 6.2:
main::kOk:
.zero 4
main::kError:
.long 1
main:
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
С другой стороны, для constexpr
это:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
mov DWORD PTR [rbp-8], 1
mov eax, 0
pop rbp
ret
Хотя в -O3
в обоих случаях я получаю ту же (оптимизированную) сборку:
main:
xor eax, eax
ret
РЕДАКТИРОВАТЬ № 2
Я пробовал этот простой код (live on Ideone):
#include <iostream>
using namespace std;
int main() {
const int k1 = 10;
constexpr int k2 = 2*k1;
cout << k2 << '\n';
return 0;
}
который показывает, что const int k1
оценивается во время компиляции, как он использовал для вычисления constexpr int k2
.
Однако для double
s существует другое поведение. Я создал отдельный вопрос для этого здесь.
Ответы
Ответ 1
Пока мы говорим о объявлении констант времени компиляции scalar integer или перечисляемых типов, нет никакой разницы между использованием const
(static const
в области видимости класса) или constexpr
,
Обратите внимание, что компиляторы должны поддерживать объекты static const int
(объявленные с постоянными инициализаторами) в постоянных выражениях, что означает, что у них нет выбора, кроме как обрабатывать такие объекты, как константы времени компиляции. Кроме того, если такие объекты остаются неиспользованными, они не требуют определения, что еще раз доказывает, что они не будут использоваться в качестве значений времени выполнения.
Кроме того, правила постоянной инициализации не позволяют инициализировать локальные объекты static const int
динамически, а это означает, что для объявления таких объектов не существует ограничения производительности. Более того, неприкосновенность интегральных объектов static
для упорядочения задач статической инициализации является очень важной особенностью языка.
constexpr
- это расширение и обобщение понятия, которое изначально было реализовано на С++ через const
с постоянным инициализатором. Для целых типов constexpr
не предлагает ничего лишнего, что уже делал const
. constexpr
просто выполняет раннюю проверку "константы" инициализатора. Однако можно сказать, что constexpr
- это функция, специально разработанная для этой цели, поэтому она лучше стилистически подходит.
Ответ 2
constexpr
переменная гарантированно имеет значение, доступное во время компиляции. тогда как члены static const
или переменная const
могут означать значение времени компиляции или значение времени выполнения. Ввод constexpr
выражает ваше намерение значения времени компиляции гораздо более явным образом, чем const
.
Еще одна вещь, в С++ 17, constexpr
переменные-члены статических данных также будут встроены. Это означает, что вы можете опустить определение строки static constexpr
, но не static const
.
Как требование в разделе комментариев, здесь более подробное описание static const
в области возможностей.
A static const
переменная в области функций практически такая же, но вместо того, чтобы иметь автоматическую продолжительность хранения, она имеет статическую продолжительность хранения. Это означает, что это в некотором роде эквивалент объявления переменной как глобальной, но доступной только в функции.
Верно, что переменная static
инициализируется при первом вызове функции, но поскольку она const
тоже, компилятор попытается встроить значение и полностью оптимизировать переменную. Таким образом, в функции , если значение известно во время компиляции для этой конкретной переменной, компилятор, скорее всего, оптимизирует его.
Однако, если значение неизвестно во время компиляции для области static const
в области видимости функции, это может сделать вашу функцию (очень маленькую) медленнее, поскольку она должна инициализировать значение во время выполнения при первом вызове функции. Кроме того, он должен проверять, инициализируется ли значение каждый раз, когда вызывается функция.
Это преимущество переменной constexpr
. Если значение во время компиляции неизвестно, это ошибка компиляции, а не более медленная функция. Тогда, если у вас нет способа определить значение переменной во время компиляции, компилятор расскажет вам об этом, и вы сможете что-то сделать.