Подтвердить что ты не робот

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 существует другое поведение. Я создал отдельный вопрос для этого здесь.

4b9b3361

Ответ 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. Если значение во время компиляции неизвестно, это ошибка компиляции, а не более медленная функция. Тогда, если у вас нет способа определить значение переменной во время компиляции, компилятор расскажет вам об этом, и вы сможете что-то сделать.