Разница между `constexpr` и` const`

В чем разница между constexpr и const ?

  • Когда я могу использовать только один из них?
  • Когда я могу использовать и то, и другое, как выбрать?

Основной смысл и синтаксис

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

  • const объявляет объект постоянным . Это подразумевает гарантию, что после инициализации значение этого объекта не изменится, и компилятор может использовать этот факт для оптимизации. Это также помогает помешать программисту писать код, который изменяет объекты, которые не должны были быть изменены после инициализации.

  • constexpr объявляет объект как подходящий для использования в том, что Стандарт вызывает постоянные выражения . Но обратите внимание, что constexpr – это не единственный способ сделать это.

При применении к функциям основное отличие заключается в следующем:

  • const может использоваться только для нестатических функций-членов, а не для функций вообще. Это дает гарантию, что функция-член не изменяет ни одного из нестатических элементов данных.

  • constexpr может использоваться как с функциями-членами, так и с не-членами, а также с конструкторами. Он объявляет функцию, пригодную для использования в постоянных выражениях . Компилятор примет его только в том случае, если функция соответствует определенным критериям (7.1.5 / 3,4), что наиболее важно (†) :

    • Тело функции должно быть не виртуальным и чрезвычайно простым: кроме typedefs и static asserts допускается только один оператор return . В случае конструктора допускаются только список инициализации, typedefs и static assert. ( = default и = delete также разрешены).
    • Начиная с C ++ 14 правила более расслаблены, что разрешено с тех пор внутри функции constexpr: объявление asm, инструкция goto, оператор с меткой, отличной от случая и значения по умолчанию, try-block, определение переменной non -литературный тип, определение переменной статической или продолжительности хранения streamов, определение переменной, для которой не выполняется инициализация.
    • Аргументы и тип возвращаемого значения должны быть буквальными типами (то есть, вообще говоря, очень простыми типами, обычно скалярами или агрегатами)

Константные выражения

Как указано выше, constexpr объявляет оба объекта, а также функции, пригодные для использования в постоянных выражениях. Постоянное выражение больше, чем просто постоянное:

  • Он может использоваться в местах, где требуется оценка времени компиляции, например, параметры шаблона и спецификаторы размера массива:

     template class fixed_size_list { /*...*/ }; fixed_size_list mylist; // X must be an integer constant expression int numbers[X]; // X must be an integer constant expression 
  • Но обратите внимание:

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

    • Объект может быть пригодным для использования в постоянных выражениях без объявления constexpr . Пример:

       int main() { const int N = 3; int numbers[N] = {1, 2, 3}; // N is constant expression return 0; } 

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

Итак, когда мне действительно нужно использовать constexpr ?

  • Объект, подобный N выше, может использоваться как постоянное выражение без объявления constexpr . Это справедливо для всех объектов:

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

    [Это из-за §5.19 / 2: константное выражение не должно содержать подвыражения, которые include «изменение значения lvalue-to-rval, если только […] значение значения целого или перечисляемого типа […]». Спасибо Ричарду Смиту за исправление моего ранее заявлял, что это верно для всех литералов.]

  • Чтобы функция была пригодна для использования в постоянных выражениях, она должна быть явно объявлена constexpr ; для него недостаточно просто удовлетворять критериям для функций постоянного выражения. Пример:

     template class list { }; constexpr int sqr1(int arg) { return arg * arg; } int sqr2(int arg) { return arg * arg; } int main() { const int X = 2; list mylist1; // OK: sqr1 is constexpr list mylist2; // wrong: sqr2 is not constexpr return 0; } 

Когда я / должен использовать / и const и constexpr вместе?

A. В объявлениях объектов. Это никогда не требуется, когда оба ключевых слова ссылаются на один и тот же объект, который должен быть объявлен. constexpr означает const .

 constexpr const int N = 5; 

такой же как

 constexpr int N = 5; 

Тем не менее, обратите внимание, что могут быть ситуации, когда ключевые слова относятся к различным частям декларации:

 static constexpr int N = 3; int main() { constexpr const int *NP = &N; return 0; } 

Здесь NP объявляется как постоянное выражение адреса, то есть указатель, который сам является константным выражением. (Это возможно, когда адрес генерируется путем применения оператора адреса к статическому / глобальному выражению константы.) Здесь constexpr как constexpr и const : constexpr всегда ссылается на объявляемое выражение (здесь NP ), а const ссылается на int (он объявляет указатель на константу). Удаление const приведет к тому, что выражение будет незаконным (потому что (a) указатель на объект, не являющийся константой, не может быть постоянным выражением, а (b) &N на самом деле является указателем на константу).

B. В объявлениях функций-членов. В C ++ 11 constexpr подразумевает const также для функций-членов. Однако это, скорее всего, изменится на C ++ 14. Согласно текущим проектам, constexpr будет подразумевать const только для объектов , а не для функций-членов, из-за предлагаемого изменения к §7.1.5 / 8. Следовательно, функция-член, объявленная в C ++ 11, как

 constexpr void f(); 

должны быть объявлены как

 constexpr void f() const; 

под C ++ 14, чтобы по-прежнему использоваться как функция const . Лучше всего отмечать, что ваши constexpr члены constexpr как const даже сейчас, чтобы избежать необходимости менять много кода позже.


(†) Условия для приемлемых функций constexpr , вероятно, будут ослаблены для C ++ 14. Предложение Ричарда Смита недавно было принято в проекте C ++ 14 .

const применяется для переменных и не позволяет им изменять ваш код.

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

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

обзор

  • const гарантирует, что программа не изменит значение объекта . Однако const не гарантирует, какой тип инициализации объект претерпевает.

    Рассматривать:

     const int mx = numeric_limits::max(); // OK: runtime initialization 

    Функция max() просто возвращает буквальное значение. Однако, поскольку инициализатор является вызовом функции, mx подвергается инициализации во время выполнения. Поэтому вы не можете использовать его как постоянное выражение :

     int arr[mx]; // error: “constant expression required” 
  • constexpr – это новое ключевое слово C ++ 11, которое избавляет вас от необходимости создавать macros и жестко закодированные литералы. Это также гарантирует при определенных условиях, что объекты подвергаются статической инициализации . Он контролирует время оценки выражения. Обеспечивая компиляцию оценки своего выражения , constexpr позволяет вам определять истинные константные выражения, которые имеют решающее значение для критически важных приложений, системного программирования, шаблонов и, вообще говоря, в любом коде, который полагается на константы времени компиляции.

Функции константного выражения

Функция константного выражения – это функция, объявленная constexpr . Его тело должно быть не виртуальным и состоять только из одного оператора return, кроме typedefs и статических утверждений. Его аргументы и возвращаемое значение должны иметь литералы. Он может использоваться с аргументами non-constant-expression, но когда это делается, результат не является постоянным выражением.

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

 constexpr int max() { return INT_MAX; } // OK constexpr long long_max() { return 2147483647; } // OK constexpr bool get_val() { bool res = false; return res; } // error: body is not just a return statement constexpr int square(int x) { return x * x; } // OK: compile-time evaluation only if x is a constant expression const int res = square(5); // OK: compile-time evaluation of square(5) int y = getval(); int n = square(y); // OK: runtime evaluation of square(y) 

Объекты с постоянным выражением

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

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

 struct S { constexpr int two(); // constant-expression function private: static constexpr int sz; // constant-expression object }; constexpr int S::sz = 256; enum DataPacket { Small = S::two(), // error: S::two() called before it was defined Big = 1024 }; constexpr int S::two() { return sz*2; } constexpr S s; int arr[s.two()]; // OK: s.two() called after its definition 

Конструкторы константных выражений

Конструктор константных выражений является конструктором, объявленным constexpr . Он может иметь список инициализации членов, но его тело должно быть пустым, кроме typedefs и static asserts. Его аргументы должны иметь буквальные типы.

Конструктор константных выражений позволяет компилятору инициализировать объект во время компиляции при условии, что аргументы конструктора являются постоянными выражениями.

 struct complex { // constant-expression constructor constexpr complex(double r, double i) : re(r), im(i) { } // OK: empty body // constant-expression functions constexpr double real() { return re; } constexpr double imag() { return im; } private: double re; double im; }; constexpr complex COMP(0.0, 1.0); // creates a literal complex double x = 1.0; constexpr complex cx1(x, 0); // error: x is not a constant expression const complex cx2(x, 1); // OK: runtime initialization constexpr double xx = COMP.real(); // OK: compile-time initialization constexpr double imaglval = COMP.imag(); // OK: compile-time initialization complex cx3(2, 4.6); // OK: runtime initialization 

Советы из книги « Эффективный современный C ++ » Скотта Мейера о constexpr :

  • constexpr объекты const и инициализируются значениями, известными во время компиляции;
  • Функции constexpr выдают результаты компиляции во время constexpr с аргументами, значения которых известны во время компиляции;
  • объекты и функции constexpr могут использоваться в более широком диапазоне контекстов, чем объекты и функции constexpr ;
  • constexpr является частью интерфейса объекта или функции.

Источник: использование constexpr для повышения безопасности, производительности и инкапсуляции в C ++ .

Согласно книге «Язык программирования C ++ 4-го Редактора» Бьярне Страуступа
const : означает примерно «Я обещаю не изменять это значение» (§7.5). Это используется в основном для указания интерфейсов, поэтому данные могут передаваться в функции, не опасаясь, что они будут изменены.
Компилятор выполняет promise, сделанное const.
constexpr : значение грубо «оценивается во время компиляции» (§10.4). Это используется прежде всего для определения констант, позволяющих
Например:

 const int dmv = 17; // dmv is a named constant int var = 17; // var is not a constant constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression const double max3 = 1.4∗square(var); //OK, may be evaluated at run time double sum(const vector&); // sum will not modify its argument (§2.2.5) vector v {1.2, 3.4, 4.5}; // v is not a constant const double s1 = sum(v); // OK: evaluated at run time constexpr double s2 = sum(v); // error : sum(v) not constant expression 

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

 constexpr double square(double x) { return x∗x; } 

Чтобы быть constexpr, функция должна быть довольно простой: просто оператор return, вычисляющий значение. Функция constexpr может использоваться для непостоянных аргументов, но когда это делается, результат не является постоянным выражением. Мы разрешаем вызывать функцию constexpr с аргументами non-constant-expression в контекстах, которые не требуют постоянных выражений, так что мы не будем определять по существу одну и ту же функцию дважды: один раз для константных выражений и один раз для переменных.
В некоторых местах постоянные выражения требуются языковыми правилами (например, границами массива (п. 2.2.5, §7.3), метками case (§ 2.2.4, §9.4.2), некоторыми аргументами шаблона (§25.2) и константы, объявленные с использованием constexpr). В других случаях оценка времени компиляции важна для производительности. Независимо от проблем с производительностью, понятие неизменности (объекта с неизменным состоянием) является важной проблемой проектирования (§10.4).

Как уже указывал @ 0x499602d2, const гарантирует, что после инициализации значение не может быть изменено, так как constexpr (введенный в C ++ 11) гарантирует, что переменная является постоянной времени компиляции.
Рассмотрим следующий пример (от LearnCpp.com):

 cout << "Enter your age: "; int age; cin >> age; const int myAge{age}; // works constexpr int someAge{age}; // error: age can only be resolved at runtime 

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

И const и constexpr означают, что их значения не могут быть изменены после их инициализации. Так, например:

 const int x1=10; constexpr int x2=10; x1=20; // ERROR. Variable 'x1' can't be changed. x2=20; // ERROR. Variable 'x2' can't be changed. 

Основное различие между const и constexpr – это время, когда их значения инициализации известны (оценены). Хотя значения const переменных можно оценивать как во время компиляции, так и во время выполнения, constexpr всегда оценивается во время выполнения. Например:

 int temp=rand(); // temp is generated by the the random generator at runtime. const int x1=10; // OK - known at compile time. const int x2=temp; // OK - known only at runtime. constexpr int x3=10; // OK - known at compile time. constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here. 

Главное преимущество знать, известно ли значение во время компиляции или времени выполнения, заключается в том, что константы времени компиляции могут использоваться, когда необходимы постоянные времени компиляции. Например, C ++ не позволяет вам указывать C-массивы с переменной длиной.

 int temp=rand(); // temp is generated by the the random generator at runtime. int array1[10]; // OK. int array2[temp]; // ERROR. 

Это означает, что:

 const int size1=10; // OK - value known at compile time. const int size2=temp; // OK - value known only at runtime. constexpr int size3=10; // OK - value known at compile time. int array3[size1]; // OK - size is known at compile time. int array4[size2]; // ERROR - size is known only at runtime time. int array5[size3]; // OK - size is known at compile time. 

Таким образом, константные переменные могут определять как константы времени компиляции, такие как size1 которые могут использоваться для указания размеров массива и констант времени выполнения, таких как size2 , которые известны только во время выполнения и не могут использоваться для определения размеров массива. С другой стороны, constexpr всегда определяет константы времени компиляции, которые могут указывать размеры массива.

Оба constexpr и constexpr могут быть применены и к функциям. Функция const должна быть функцией-членом (метод, оператор), где применение ключевого слова const означает, что метод не может изменять значения своих (не статических) полей. Например.

 class test { int x; void function1() { x=100; // OK. } void function2() const { x=100; // ERROR. The const methods can't change the values of object fields. } }; 

constexpr – это другое понятие. Он отмечает функцию (член или нечлен) как функцию, которую можно оценить во время выполнения, если в качестве аргументов передаются константы времени компиляции . Например, вы можете написать это.

 constexpr int func_constexpr(int X, int Y) { return(X*Y); } int func(int X, int Y) { return(X*Y); } int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time. int array2[func(10,20)]; // ERROR - func() is not a constexpr function. int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time. 

Кстати, функции constexpr являются регулярными функциями C ++, которые можно вызывать, даже если передаются непостоянные аргументы. Но в этом случае вы получаете значения non-constexpr.

 int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime. constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time. 

constexpr может также применяться к функциям (методам) constexpr , операторам и даже конструкторам. Например.

 class test2 { static constexpr int function(int value) { return(value+1); } void f() { int x[function(10)]; } }; 

Более «сумасшедший» образец.

 class test3 { public: int value; // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time. constexpr int getvalue() const { return(value); } constexpr test3(int Value) : value(Value) { } }; constexpr test3 x(100); // OK. Constructor is constexpr. int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time. 

const int var может быть динамически установлена ​​на значение во время выполнения, и как только она будет установлена ​​на это значение, она больше не может быть изменена.

constexpr int var не может динамически устанавливаться во время выполнения, а скорее во время компиляции. И как только оно будет установлено на это значение, оно больше не может быть изменено.

Вот пример:

 int main(int argc, char*argv[]) { const int p = argc; // p = 69; // cannot change p because it is a const // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time constexpr int r = 2^3; // this works! // r = 42; // same as const too, it cannot be changed } 

Фрагмент выше компилируется отлично, и я прокомментировал те, которые вызывают ошибку.

  • Возможно ли is_constexpr в C ++ 11?
  • статический член constexpr того же типа, что и class
  • enum vs constexpr для реальных статических констант внутри classов
  • constexpr инициализирует статический член, используя статическую функцию
  • constexpr и инициализация статического константного указателя void с реинтерпретом, который компилятор прав?
  • Constexpr Математические функции
  • C ++ concat два строковых литерала `const char`
  • Вычисление длины строки C во время компиляции. Это действительно констебр?
  • Создать N-элементный массив constexpr в C ++ 11
  • C ++ 11: вычисление времени вычисления массива
  • Когда функция constexpr получает оценку во время компиляции?
  • Interesting Posts

    ServiceStack: RESTful Resource Versioning

    Как изменить размер видео, чтобы уменьшить его с помощью FFmpeg

    printf переменной size_t с идентификаторами lld, ld и d

    Как предотвратить пробелы между абзацем и большими изображениями в Word

    Разница между литьем и использованием метода Convert.To ()

    Как подключить javadoc или источники к банкам в папке libs?

    ProcessInfo и RedirectStandardOutput

    Gpaste получить как STDIN для youtube-dl

    Как вы вычисляете день года на определенную дату в Objective-C?

    Excel VBA – электронная почта не отправляется при блокировке компьютера

    Элемент списка списка проведите пальцем влево и проведите пальцем вправо?

    Машина VirtualBox с именем «homestead» уже существует

    Невозможно остановить сигнал вызова из другой активности

    Равномерно разнесенные пункты меню на Панели инструментов

    «Примечание: неопределенная переменная», «Примечание: неопределенный индекс» и «Примечание: неопределенное смещение» с использованием PHP

    Давайте будем гением компьютера.