Поиск…
Вступление
constexpr
- это ключевое слово, которое может использоваться для обозначения значения переменной как постоянного выражения, функции, потенциально пригодной для использования в постоянных выражениях, или (поскольку C ++ 17) оператор if имеет только одну из своих ветвей, выбранных для компиляции.
замечания
constexpr
слово constexpr
было добавлено в C ++ 11, но в течение нескольких лет после публикации стандарта C ++ 11 не все основные компиляторы поддерживали его. в то время, когда был опубликован стандарт C ++ 11. На момент публикации C ++ 14 все основные компиляторы поддерживают constexpr
.
переменные constexpr
Переменная, объявленная constexpr
, неявно const
и ее значение может использоваться как постоянное выражение.
Сравнение с #define
constexpr
- это безопасная замена для выражений времени компиляции на основе #define
. С constexpr
выражение с constexpr
времени компиляции заменяется результатом. Например:
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
но у него есть свои подводные камни.
Поэтому просто используйте:
int main()
{
constexpr int size = 10;
int arr[size];
}
Выражение constexpr
должно оцениваться до значения времени компиляции. Таким образом, вы не можете использовать:
constexpr int size = abs(10);
Если функция ( abs
) сама возвращает constexpr
.
Все базовые типы могут быть инициализированы с помощью constexpr
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
Интересно, что и удобно, вы также можете использовать auto
:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
функции constexpr
Функция, объявленная как constexpr
является неявно встроенной, и constexpr
такой функции потенциально дает постоянные выражения. Например, следующая функция, если вызывается с постоянными аргументами выражения, также дает константное выражение:
constexpr int Sum(int a, int b)
{
return a + b;
}
Таким образом, результат вызова функции может использоваться как constexpr
массива или аргумент шаблона или инициализировать переменную constexpr
:
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
заключается в том, что вы можете использовать ее как обычные функции:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
теперь не будет функцией constexpr
, она будет скомпилирована как обычная функция, принимая переменные (непостоянные) аргументы и возвращая непостоянное значение. Вам не нужно писать две функции.
Это также означает, что если вы попытаетесь назначить такой вызов неконстантной переменной, он не будет компилироваться:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
Причина проста: constexpr
должна constexpr
только константа времени компиляции. Однако вышеупомянутый вызов функции делает Sum
не constexpr
(значение R не является constexpr
, но L-значение объявляет себя constexpr
).
Функция constexpr
также должна возвращать константу времени компиляции. Следующее не будет компилироваться:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
Поскольку a1
является переменной , отличной от constexpr, и запрещает функции быть истинной функцией constexpr
. Что делает его constexpr
и присвоение ему также не будет работать - так как значение a
a
(входящего параметра) до сих пор еще не известно:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
Кроме того, следующее не будет компилироваться:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
Поскольку abs(a)
не является постоянным выражением (даже abs(10)
не будет работать, так как abs
не возвращает constexpr int
!
Как насчет этого?
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
Оператор 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
}