Const vs constexpr для переменных

Есть ли разница между следующими определениями?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

Если нет, какой стиль предпочтительнее в С++ 11?

Ответ 1

Я считаю, что есть разница. Позвольте переименовать их, чтобы мы могли легче говорить о них:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

Оба PI1 и PI2 являются постоянными, то есть вы не можете их изменять. Однако только PI2 является константой времени компиляции. Он должен инициализироваться в собранном времени. PI1 может быть инициализирован во время компиляции или времени выполнения. Кроме того, только PI2 может использоваться в контексте, который требует постоянной времени компиляции. Например:

constexpr double PI3 = PI1;  // error

а

constexpr double PI3 = PI2;  // ok

и

static_assert(PI1 == 3.141592653589793, "");  // error

а

static_assert(PI2 == 3.141592653589793, "");  // ok

Что вы должны использовать? Используйте то, что соответствует вашим потребностям. Вы хотите, чтобы у вас была константа времени компиляции, которая может использоваться в контекстах, где требуется постоянная времени компиляции? Вы хотите, чтобы иметь возможность инициализировать его с помощью вычислений, выполненных во время выполнения? Etc.

Ответ 2

Здесь нет никакой разницы, но имеет значение, если у вас есть тип, у которого есть конструктор.

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0 является константой, но она не обещает быть инициализированной во время компиляции. s1 отмечен constexpr, поэтому он является константой и, поскольку конструктор S также помечен constexpr, он будет инициализирован во время компиляции.

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

Ответ 3

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

int sz;
constexpr auto arraySize1 = sz;    // error! sz value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

Обратите внимание, что const не предоставляет ту же гарантию, что и constexpr, потому что const объекты не обязательно должны быть инициализированы значениями, известными во время компиляции.

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize value unknown at compilation

Все объекты constexpr являются константами, но не все объекты const являются constexpr.

Если вы хотите, чтобы компиляторы гарантировали, что переменная имеет значение, которое может быть используется в контекстах, требующих константы времени компиляции, инструментом для достижения является constexpr, а не const.

Ответ 4

Символьная константа A constexpr должна иметь значение, известное во время компиляции. Например:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

Чтобы обрабатывать случаи, когда значение "переменной", которое инициализируется значением, которое неизвестно во время компиляции, но никогда не изменяется после инициализации, С++ предлагает вторую форму константы (a const). Например:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

Такие переменные const очень часто встречаются по двум причинам:

  • У С++ 98 не было constexpr, поэтому люди использовали const.
  • Элемент списка "Переменные", которые не являются постоянными выражениями (их значение неизвестно во время компиляции), но не меняют значения после инициализация сама по себе является весьма полезной.