Поиск…


Вступление

constexpr - это ключевое слово, которое может использоваться для обозначения значения переменной как постоянного выражения, функции, потенциально пригодной для использования в постоянных выражениях, или (поскольку C ++ 17) оператор if имеет только одну из своих ветвей, выбранных для компиляции.

замечания

constexpr слово constexpr было добавлено в C ++ 11, но в течение нескольких лет после публикации стандарта C ++ 11 не все основные компиляторы поддерживали его. в то время, когда был опубликован стандарт C ++ 11. На момент публикации C ++ 14 все основные компиляторы поддерживают constexpr .

переменные constexpr

Переменная, объявленная constexpr , неявно const и ее значение может использоваться как постоянное выражение.

Сравнение с #define

constexpr - это безопасная замена для выражений времени компиляции на основе #define . С constexpr выражение с constexpr времени компиляции заменяется результатом. Например:

C ++ 11
int main()
{
   constexpr int N = 10 + 2;
   cout << N;
}

выдает следующий код:

cout << 12;

Макрос компиляции, основанный на предварительном процессоре, будет отличаться. Рассматривать:

#define N 10 + 2

int main()
{
    cout << N;
}

будет производить:

cout << 10 + 2;

который, очевидно, будет преобразован в cout << 10 + 2; , Однако компилятору придется больше работать. Кроме того, это создает проблему, если не используется правильно.

Например (с #define ):

cout << N * 2;

формы:

cout << 10 + 2 * 2; // 14

Но предварительно оцененный constexpr правильно дал бы 24 .

Сравнение с const

const переменная - это переменная, для которой требуется память для хранения. constexpr не делает. constexpr создает constexpr времени компиляции, которая не может быть изменена. Вы можете утверждать, что const также не может быть изменен. Но учтите:

int main()
{
   const int size1 = 10;
   const int size2 = abs(10);

   int arr_one[size1]; 
   int arr_two[size2]; 
}

С большинством компиляторов второй оператор не работает (может работать с GCC, например). Размер любого массива, как вы знаете, должен быть постоянным выражением (т. Е. Результатом значения времени компиляции). Второй переменной size2 присваивается определенное значение, которое определяется во время выполнения (даже если вы знаете, что это 10 , для компилятора это не время компиляции).

Это означает , что const может или не может быть истинной константой времени компиляции. Вы не можете гарантировать или обеспечить, чтобы конкретное значение const абсолютно компилируемым. Вы можете использовать #define но у него есть свои подводные камни.

Поэтому просто используйте:

C ++ 11
int main()
{
    constexpr int size = 10;

    int arr[size];
}

Выражение constexpr должно оцениваться до значения времени компиляции. Таким образом, вы не можете использовать:

C ++ 11
constexpr int size = abs(10);

Если функция ( abs ) сама возвращает constexpr .

Все базовые типы могут быть инициализированы с помощью constexpr .

C ++ 11
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";

Интересно, что и удобно, вы также можете использовать auto :

C ++ 11
constexpr auto domain = ".COM";  // const char * const domain = ".COM"
constexpr auto PI = 3.14;        // constexpr double

функции constexpr

Функция, объявленная как constexpr является неявно встроенной, и constexpr такой функции потенциально дает постоянные выражения. Например, следующая функция, если вызывается с постоянными аргументами выражения, также дает константное выражение:

C ++ 11
constexpr int Sum(int a, int b)
{
    return a + b;
}

Таким образом, результат вызова функции может использоваться как constexpr массива или аргумент шаблона или инициализировать переменную constexpr :

C ++ 11
int main()
{
    constexpr int S = Sum(10,20);
   
    int Array[S];
    int Array2[Sum(20,30)]; // 50 array size, compile time
}

Обратите внимание: если вы удаляете constexpr из спецификации типа возвращаемого значения, присваивание S не будет работать, так как S является constexpr и ей должна быть назначена constexpr времени компиляции. Аналогично, размер массива также не будет константным выражением, если функция Sum не constexpr .

Интересная вещь о constexpr заключается в том, что вы можете использовать ее как обычные функции:

C ++ 11
int a = 20;
auto sum = Sum(a, abs(-20));

Sum теперь не будет функцией constexpr , она будет скомпилирована как обычная функция, принимая переменные (непостоянные) аргументы и возвращая непостоянное значение. Вам не нужно писать две функции.

Это также означает, что если вы попытаетесь назначить такой вызов неконстантной переменной, он не будет компилироваться:

C ++ 11
int a = 20;
constexpr auto sum = Sum(a, abs(-20));

Причина проста: constexpr должна constexpr только константа времени компиляции. Однако вышеупомянутый вызов функции делает Sum не constexpr (значение R не является constexpr , но L-значение объявляет себя constexpr ).


Функция constexpr также должна возвращать константу времени компиляции. Следующее не будет компилироваться:

C ++ 11
constexpr int Sum(int a, int b)
{
    int a1 = a;     // ERROR
    return a + b;
}

Поскольку a1 является переменной , отличной от constexpr, и запрещает функции быть истинной функцией constexpr . Что делает его constexpr и присвоение ему также не будет работать - так как значение a a (входящего параметра) до сих пор еще не известно:

C ++ 11
constexpr int Sum(int a, int b)
{
   constexpr int a1 = a;     // ERROR
   ..

Кроме того, следующее не будет компилироваться:

C ++ 11
constexpr int Sum(int a, int b)
{
   return abs(a) + b; // or abs(a) + abs(b)
}

Поскольку abs(a) не является постоянным выражением (даже abs(10) не будет работать, так как abs не возвращает constexpr int !

Как насчет этого?

C ++ 11
constexpr int Abs(int v)
{
    return v >= 0 ? v : -v;
}

constexpr int Sum(int a, int b)
{
    return Abs(a) + b;
}

Мы создали собственную функцию Abs которая является constexpr , а тело Abs также не нарушает никакого правила. Кроме того, на сайте вызова (внутри Sum ) выражение вычисляется как constexpr . Следовательно, вызов Sum(-10, 20) будет выражением постоянной времени компиляции, результатом которого будет 30 .

Статический оператор if

C ++ 17

Оператор if constexpr может использоваться для условной компиляции кода. Условие должно быть постоянным выражением. Отключенная ветвь не отбрасывается. Отбрасываемый оператор внутри шаблона не создается. Например:

template<class T, class ... Rest>
void g(T &&p, Rest &&...rs)
{
  // ... handle p
  if constexpr (sizeof...(rs) > 0)
    g(rs...);  // never instantiated with an empty argument list
}

Кроме того, переменные и функции, которые используются odr только внутри отброшенных операторов, не требуются для определения, а отбрасываемые операторы return не используются для вычитания типа функции.

if constexpr отличается от #ifdef . #ifdef условно компилирует код, но только на основе условий, которые могут быть оценены во время предварительной обработки. Например, #ifdef не может использоваться для условной компиляции кода в зависимости от значения параметра шаблона. С другой стороны, if constexpr не может использоваться для отбрасывания синтаксически недействительного кода, в то время как #ifdef может.

if constexpr(false) {
    foobar;  // error; foobar has not been declared
    std::vector<int> v("hello, world");  // error; no matching constructor
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow