Может быть, эта статья из boost/hanaбиблиотеки может рассказать о некоторых constexprпроблемах, которые вы можете использовать, constexprа где нет: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry
@ 0x499602D2 « просто означает, что значение не может быть изменено ». Для скаляра, инициализированного литералом, значение, которое нельзя изменить , также является постоянной времени компиляции.
любопытный парень
@curiousguy Да, мой комментарий был очень упрощен. По общему признанию, я был constexpr
новичком
Ответы:
587
Основное значение и синтаксис
Оба ключевых слова могут быть использованы в объявлении объектов, а также функций. Основное отличие применительно к объектам заключается в следующем:
constобъявляет объект как константу . Это подразумевает гарантию того, что после инициализации значение этого объекта не изменится, и компилятор может использовать этот факт для оптимизации. Это также помогает запретить программисту писать код, который изменяет объекты, которые не должны были изменяться после инициализации.
constexprобъявляет объект пригодным для использования в том, что стандарт называет константными выражениями . Но учтите, что constexprэто не единственный способ сделать это.
Применительно к функциям основное отличие заключается в следующем:
constможет использоваться только для нестатических функций-членов, но не для функций в целом. Это дает гарантию, что функция-член не изменяет ни один из нестатических элементов данных.
constexprможет использоваться как с членами, так и с не членами, а также с конструкторами. Он объявляет функцию пригодной для использования в константных выражениях . Компилятор примет его, только если функция соответствует определенным критериям (7.1.5 / 3,4), наиболее важно (†) :
Тело функции должно быть не виртуальным и чрезвычайно простым: кроме определения типов и статических returnутверждений, допускается только один оператор. В случае конструктора разрешены только список инициализации, typedefs и static assert. ( = defaultи = deleteтоже разрешено.)
Начиная с C ++ 14, правила более смягчены, что разрешено с тех пор внутри функции constexpr: asmобъявление, gotoоператор, оператор с меткой, отличной от caseи default, try-block, определение переменной не-литерала тип, определение переменной статической или длительности хранения потока, определение переменной, для которой не выполняется инициализация.
Аргументы и возвращаемый тип должны быть литеральными типами (то есть, вообще говоря, очень простыми типами, обычно скалярами или агрегатами).
Постоянные выражения
Как сказано выше, constexprобъявляет оба объекта, а также функции, пригодные для использования в константных выражениях. Постоянное выражение не просто константа:
Он может использоваться в местах, где требуется оценка во время компиляции, например, параметры шаблона и спецификаторы размера массива:
template<int N>class fixed_size_list
{/*...*/};
fixed_size_list<X> mylist;// X must be an integer constant expressionint numbers[X];// X must be an integer constant expression
Но обратите внимание:
Объявление чего-либо как constexprне обязательно гарантирует, что оно будет оценено во время компиляции. Он может использоваться для таких целей, но может использоваться и в других местах, которые оцениваются также во время выполнения.
Объект может быть пригоден для использования в константных выражениях без объявления constexpr. Пример:
int main(){constint N =3;int numbers[N]={1,2,3};// N is constant expression}
Это возможно, потому что N, будучи постоянным и инициализированным во время объявления с литералом, удовлетворяет критериям для постоянного выражения, даже если оно не объявлено constexpr.
Так, когда я действительно должен использовать constexpr?
Объект , как Nвыше , может быть использован в качестве постоянного выражения , не объявляются constexpr. Это верно для всех объектов, которые:
const
целочисленного или перечислимого типа и
инициализируется во время объявления выражением, которое само является константным выражением
[Это связано с §5.19 / 2: константное выражение не должно включать подвыражение, которое включает «модификацию lvalue-to-rvalue, если […] glvalue целочисленного или перечислимого типа […]» Спасибо Ричарду Смиту за исправление моего ранее утверждали, что это верно для всех литеральных типов.]
Чтобы функция была пригодна для использования в константных выражениях, она должна быть явно объявлена constexpr; недостаточно просто удовлетворять критериям для функций с постоянным выражением. Пример:
template<int N>classlist{};constexprint sqr1(int arg){return arg * arg;}int sqr2(int arg){return arg * arg;}int main(){constint X =2;list<sqr1(X)> mylist1;// OK: sqr1 is constexprlist<sqr2(X)> mylist2;// wrong: sqr2 is not constexpr}
Когда я могу / должен использовать оба, constи constexprвместе?
А. В объявлениях объекта. Это никогда не требуется, когда оба ключевых слова ссылаются на один и тот же объект, который должен быть объявлен. constexprподразумевает const.
constexprconstint N =5;
такой же как
constexprint N =5;
Однако обратите внимание, что могут быть ситуации, когда каждое ключевое слово ссылается на разные части объявления:
staticconstexprint N =3;int main(){constexprconstint*NP =&N;}
Здесь NPобъявляется как адрес-константа-выражение, то есть указатель, который сам является константным выражением. (Это возможно , когда адрес генерируется путем применения оператора адреса к / глобальному выражению постоянная статическим.) Здесь, как constexprи constтребуется: constexprвсегда относится к выражению объявляются (здесь NP), в то время как constотносится к int(это объявляет pointer- к сопзЬ). Удаление constможет сделать выражение недопустимым (потому что (a) указатель на неконстантный объект не может быть константным выражением, а (b) &Nфактически является указателем на константу).
Б. В объявлениях функций-членов. В C ++ 11 constexprподразумевается const, а в C ++ 14 и C ++ 17 это не так. Функция-член, объявленная в C ++ 11 как
constexprvoid f();
должен быть объявлен как
constexprvoid f()const;
под C ++ 14, чтобы все еще быть пригодным для использования в качестве constфункции.
ИМО «необязательно оценивать во время компиляции» менее полезно, чем думать о них как «оцениваемое во время компиляции». Ограничения для константного выражения означают, что компилятору будет относительно легко его оценить. Компилятор должен пожаловаться, если эти ограничения не выполняются. Поскольку побочных эффектов нет, вы никогда не сможете определить разницу, «оценивал» это компилятор или нет.
aschepler
10
@aschepler Конечно. Мое главное замечание заключается в том, что если вы вызываете constexprфункцию с неконстантным выражением, например с обычной переменной, это совершенно законно, и функция будет использоваться, как и любая другая функция. Он не будет оцениваться во время компиляции (потому что не может). Возможно, вы думаете, что это очевидно - но если бы я сказал, что функция, объявленная как constexpr, всегда будет оцениваться во время компиляции, она может быть неверно интерпретирована.
Джогоджапан
5
Да, я говорил об constexprобъектах, а не функциях. Мне нравится думать constexprоб объектах как о принудительной оценке значений времени компиляции, а constexprо функциях - как о том, что функция может быть оценена во время компиляции или во время выполнения в зависимости от ситуации.
aschepler
2
Исправление: «const» - это только ограничение, которое ВЫ не можете изменить значение переменной; он не дает никаких обещаний, что значение не изменится (т.е. кем-то другим). Это свойство записи, а не свойство чтения.
Джаред Грабб
3
Это предложение: оно дает гарантию, что функция-член не изменяет ни один из нестатических элементов данных. пропускает одну важную деталь Члены, помеченные как, mutableтакже могут быть изменены constфункциями-членами.
Всезнающий
119
constприменяется к переменным и предотвращает их изменение в вашем коде.
constexprсообщает компилятору, что это выражение приводит к значению постоянной времени компиляции , поэтому его можно использовать в таких местах, как длина массива, присвоение constпеременных и т. д. Ссылка, предоставленная Oli, имеет много прекрасных примеров.
По сути, они представляют собой две разные концепции и могут (и должны) использоваться вместе.
Функция max()просто возвращает буквальное значение. Однако, поскольку инициализатор является вызовом функции, он mxподвергается инициализации во время выполнения. Следовательно, вы не можете использовать его как константное выражение :
int arr[mx];// error: “constant expression required”
constexprэто новое ключевое слово C ++ 11, которое избавляет вас от необходимости создавать макросы и жестко закодированные литералы. Это также гарантирует, при определенных условиях, что объекты подвергаются статической инициализации . Он контролирует время оценки выражения. Обеспечивая оценку его выражения во время компиляции , constexprпозволяет вам определять истинные константные выражения, которые имеют решающее значение для критичных ко времени приложений, системного программирования, шаблонов и, вообще говоря, в любом коде, который опирается на константы времени компиляции.
Функции с постоянными выражениями
Функция с постоянным выражением - это объявленная функция constexpr. Его тело должно быть не виртуальным и состоять только из одного оператора return, кроме typedefs и static asserts. Его аргументы и возвращаемое значение должны иметь литеральные типы. Его можно использовать с аргументами без константных выражений, но когда это сделано, результат не является константным выражением.
Функция постоянного выражения предназначена для замены макросов и жестко закодированных литералов без ущерба для производительности или безопасности типов.
constexprint max(){return INT_MAX;}// OKconstexprlong long_max(){return2147483647;}// OKconstexprbool get_val(){bool res =false;return res;}// error: body is not just a return statementconstexprint square(int x){return x * x;}// OK: compile-time evaluation only if x is a constant expressionconstint res = square(5);// OK: compile-time evaluation of square(5)int y = getval();int n = square(y);// OK: runtime evaluation of square(y)
Объекты с постоянным выражением
Объект с постоянным выражением является объявленным объектом constexpr. Он должен быть инициализирован постоянным выражением или значением, созданным конструктором с постоянным выражением с аргументами постоянного выражения.
Объект константного выражения ведет себя так, как если бы он был объявлен const, за исключением того, что он требует инициализации перед использованием, а его инициализатор должен быть константным выражением. Следовательно, объект константного выражения всегда можно использовать как часть другого константного выражения.
struct S
{constexprint two();// constant-expression functionprivate:staticconstexprint sz;// constant-expression object};constexprint S::sz =256;enumDataPacket{Small= S::two(),// error: S::two() called before it was definedBig=1024};constexprint S::two(){return sz*2;}constexpr S s;int arr[s.two()];// OK: s.two() called after its definition
Константные выражения
Постоянное выражение-конструктор является конструктором объявлена constexpr. Он может иметь список инициализации члена, но его тело должно быть пустым, кроме typedefs и статических утверждений. Его аргументы должны иметь буквенные типы.
Конструктор с постоянным выражением позволяет компилятору инициализировать объект во время компиляции, при условии, что все аргументы конструктора являются константными выражениями.
struct complex
{// constant-expression constructorconstexpr complex(double r,double i): re(r), im(i){}// OK: empty body// constant-expression functionsconstexprdouble real(){return re;}constexprdouble imag(){return im;}private:double re;double im;};constexpr complex COMP(0.0,1.0);// creates a literal complexdouble x =1.0;constexpr complex cx1(x,0);// error: x is not a constant expressionconst complex cx2(x,1);// OK: runtime initializationconstexprdouble xx = COMP.real();// OK: compile-time initializationconstexprdouble imaglval = COMP.imag();// OK: compile-time initialization
complex cx3(2,4.6);// OK: runtime initialization
Подсказки из книги Скотта Мейерса « Эффективное современное С ++ » о constexpr:
constexpr объекты являются постоянными и инициализируются значениями, известными во время компиляции;
constexpr функции выдают результаты времени компиляции при вызове с аргументами, значения которых известны во время компиляции;
constexprобъекты и функции могут использоваться в более широком диапазоне контекстов, чем не- constexprобъекты и функции;
constexpr является частью интерфейса объекта или функции.
Спасибо за отличный пример кода, показывающий различные ситуации. Как ни крути другие объяснения, я обнаружил, что просмотр кода в действии гораздо более полезен и понятен. Это действительно помогло укрепить мое понимание происходящего.
RTHarston
35
В соответствии с книгой Бьярна Страуструпа «Язык программирования C ++ 4-й редакции»
• const : примерно означает «Я обещаю не менять это значение» (§7.5). Это используется главным образом для указания интерфейсов, так что данные могут передаваться функциям, не опасаясь их изменения.
Компилятор выполняет обещание, данное const.
• constexpr : примерно означает «быть оцененным во время компиляции» (§10.4). Это используется в первую очередь для указания констант,
например:
constint dmv =17;// dmv is a named constantint var =17;// var is not a constantconstexprdouble max1 =1.4*square(dmv);// OK if square(17) is a constant expressionconstexprdouble max2 =1.4∗square(var);// error : var is not a constant expressionconstdouble max3 =1.4∗square(var);//OK, may be evaluated at run timedouble sum(constvector<double>&);// sum will not modify its argument (§2.2.5)vector<double> v {1.2,3.4,4.5};// v is not a constantconstdouble s1 = sum(v);// OK: evaluated at run timeconstexprdouble s2 = sum(v);// error : sum(v) not constant expression
Чтобы функция могла использоваться в константном выражении, то есть в выражении, которое будет оцениваться компилятором, должно быть определено constexpr . Например:
constexprdouble square(double x){return x∗x;}
Чтобы быть constexpr, функция должна быть довольно простой: просто оператор возврата, вычисляющий значение. Функция constexpr может использоваться для непостоянных аргументов, но когда это сделано, результат не является константным выражением. Мы разрешаем вызывать функцию constexpr с аргументами неконстантных выражений в контекстах, которые не требуют константных выражений, поэтому мы не должны определять по существу одну и ту же функцию дважды: один раз для константных выражений и один раз для переменных.
В некоторых местах константные выражения требуются правилами языка (например, границы массивов (§2.2.5, §7.3), метки case (§2.2.4, §9.4.2), некоторые аргументы шаблона (§25.2) и константы, объявленные с помощью constexpr). В других случаях оценка времени компиляции важна для производительности. Независимо от проблем производительности, понятие неизменности (объекта с неизменяемым состоянием) является важной проблемой проектирования (§10.4).
Есть еще проблемы с производительностью. Кажется, что функция constexpr при оценке во время выполнения может работать медленнее, чем не-constexpr версия функции. Также, если у нас есть постоянное значение, мы должны предпочесть «const» или «constexpr»? (больше стиль, сгенерированный сборкой вопросов выглядит одинаково)
CoffeDeveloper
31
И то, constи другое constexprможно применять к переменным и функциям. Хотя они похожи друг на друга, на самом деле это очень разные понятия.
Оба constи constexprозначают, что их значения не могут быть изменены после их инициализации. Так, например:
constint x1=10;constexprint 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.constint x1=10;// OK - known at compile time.constint x2=temp;// OK - known only at runtime.constexprint x3=10;// OK - known at compile time.constexprint 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.
Итак, это означает, что:
constint size1=10;// OK - value known at compile time.constint size2=temp;// OK - value known only at runtime.constexprint 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.
Таким образом, constпеременные могут определять как константы времени компиляции, подобные тем, size1которые можно использовать для указания размеров массива, так и константы времени выполнения, подобные size2тем, которые известны только во время выполнения и не могут использоваться для определения размеров массива. С другой стороны, constexprвсегда определяйте константы времени компиляции, которые могут указывать размеры массива.
И то, constи другое 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это другая концепция. Он отмечает функцию (член или не член) как функцию, которая может быть оценена во время компиляции, если в качестве аргументов переданы константы времени компиляции . Например, вы можете написать это.
constexprint 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 ++, которые можно вызывать, даже если переданы непостоянные аргументы. Но в этом случае вы получаете значения не-constexpr.
int value1=func_constexpr(10,rand());// OK. value1 is non-constexpr value that is evaluated in runtime.constexprint value2=func_constexpr(10,rand());// ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.
constexprТакже может быть применено к функциям членов (методы), операторов и даже конструкторов. Например.
class test2
{staticconstexprint 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.constexprint getvalue()const{return(value);}constexpr test3(intValue): value(Value){}};constexpr test3 x(100);// OK. Constructor is constexpr.intarray[x.getvalue()];// OK. x.getvalue() is constexpr and can be evaluated at compile time.
Кроме того, в C, constexpr intсуществует, но это пишетсяconst int
curiousguy
8
Как уже указывалось @ 0x499602d2, constтолько гарантирует, что значение не может быть изменено после инициализации, поскольку as constexpr(введено в C ++ 11) гарантирует, что переменная является постоянной времени компиляции.
Рассмотрим следующий пример (из LearnCpp.com):
cout <<"Enter your age: ";int age;
cin >> age;constint myAge{age};// worksconstexprint someAge{age};// error: age can only be resolved at runtime
Значение A const int varможно динамически установить в значение во время выполнения, и как только оно будет установлено в это значение, его уже нельзя будет изменить.
A constexpr int varне может быть динамически установлен во время выполнения, а во время компиляции. И как только оно будет установлено на это значение, его уже нельзя будет изменить.
Вот хороший пример:
int main(int argc,char*argv[]){constint 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 constexprint r =2^3;// this works!// r = 42; // same as const too, it cannot be changed}
Приведенный выше фрагмент прекрасно компилируется, и я прокомментировал те, которые приводят к ошибке.
Ключевые понятия, на которые следует обратить внимание, это понятия compile timeи run time. В C ++ были введены новые нововведения, предназначенные для максимально возможного количества ** know **определенных вещей во время компиляции для улучшения производительности во время выполнения.
Я не думаю, что какой-либо из ответов действительно прояснит, какие именно побочные эффекты у него есть, или действительно, что это такое.
constexprи constв namespace / file-scope идентичны, когда инициализируются литералом или выражением; но с функцией, constможет быть инициализирован любой функцией, но constexprинициализирован non-constexpr (функция, которая не помечена как constexpr или выражение не constexpr), будет генерировать ошибку компилятора. И то constexprи другое constнеявно является внутренней связью для переменных (ну, на самом деле, они не доживут до стадии связывания, если компилируют -O1 и более сильные, и staticне заставляют компилятор испускать внутренний (локальный) символ компоновщика для constили constexprкогда в -O1 или более сильный, единственный раз, когда это происходит, если вы берете адрес переменной. constИ constexprбудет внутренним символом, если не выражено с помощью externieextern constexpr/const int i = 3;должен быть использован). В функции constexprделает функцию постоянно никогда не достигающей стадии связывания (независимо от externили inlineв определении или -O0 или -Ofast), тогда как constникогда не достигает, staticи inlineтолько влияет на -O1 и выше. Когда const/ constexprпеременная инициализируется constexprфункцией, загрузка всегда оптимизируется с любым флагом оптимизации, но она никогда не оптимизируется, если функция только staticили inline, или если переменная не является const/ constexpr.
Стандартная компиляция (-O0)
#include<iostream>constexprint multiply (int x,int y){return x * y;}externconstint val = multiply(10,10);int main (){
std::cout << val;}
компилируется в
val:.long100//extra external definition supplied due to extern
main:
push rbp
mov rbp, rsp
mov esi,100//substituted in as an immediate
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char>>::operator<<(int)
mov eax,0
pop rbp
ret
__static_initialization_and_destruction_0(int,int):...
Однако
#include<iostream>constint multiply (int x,int y){return x * y;}constint val = multiply(10,10);//constexpr is an errorint main (){
std::cout << val;}
Компилируется в
multiply(int,int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
mov eax, DWORD PTR [rbp-4]
imul eax, DWORD PTR [rbp-8]
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov eax, DWORD PTR val[rip]
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char>>::operator<<(int)
mov eax,0
pop rbp
ret
__static_initialization_and_destruction_0(int,int):...
mov esi,10
mov edi,10
call multiply(int,int)
mov DWORD PTR val[rip], eax
Это ясно показывает, что constexprинициализация const/constexprпеременной file-scope происходит во время компиляции и не генерирует глобальный символ, тогда как ее использование не вызывает инициализацию раньше mainво время выполнения.
constexprФункции также могут быть вызваны из других constexprфункций для того же результата. constexprфункция также предотвращает использование чего-либо, что не может быть сделано во время компиляции в функции; например, звонок <<оператору на std::cout.
constexprв области видимости блока ведет себя так же, что выдает ошибку, если инициализируется функцией non-constexpr; значение также подставляется сразу.
В конце концов, его основное предназначение похоже на встроенную функцию C, но она эффективна только тогда, когда функция используется для инициализации переменных области файла (что функции не могут делать на C, но они могут на C ++, потому что она позволяет динамическую инициализацию file- переменные области видимости), за исключением того, что функция не может экспортировать глобальный / локальный символ также в компоновщик, даже используя extern/static, что вы могли бы использовать inlineна C; Функции назначения переменных в области блока могут быть встроены просто с помощью оптимизации -O1 без использования constexprC и C ++.
Хорошая точка на компоновщик. Можно ли в целом считать более безопасным использование constexpr, поскольку это приводит к уменьшению утечек символов?
Нил МакГилл
1
@NeilMcGill на самом деле не потому, что inline и static заставят компилятор не выдавать локальный символ для умножения, если компилируется с использованием -O1 или более сильной. Constexpr является единственным, который оптимизирует нагрузку для val, но в остальном он идентичен положению static или inline перед функцией. Я забыл еще кое-что. Constexpr - единственное ключевое слово, которое не генерирует символ для функции на -O0, статической и встроенной функции
Льюис Келси
1
Прежде всего, оба являются классификаторами в C ++. Объявленная переменная const должна быть инициализирована и не может быть изменена в будущем. Следовательно, обычно переменная, объявленная как const, будет иметь значение даже до компиляции.
Но для constexpr это немного другое.
Для constexpr вы можете указать выражение, которое можно было бы оценить во время компиляции программы.
Очевидно, что переменная, объявленная как constexper, не может быть изменена в будущем так же, как const.
constexpr
создает постоянную времени компиляции;const
просто означает, что значение не может быть изменено.boost/hana
библиотеки может рассказать о некоторыхconstexpr
проблемах, которые вы можете использовать,constexpr
а где нет: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…constexpr
Ответы:
Основное значение и синтаксис
Оба ключевых слова могут быть использованы в объявлении объектов, а также функций. Основное отличие применительно к объектам заключается в следующем:
const
объявляет объект как константу . Это подразумевает гарантию того, что после инициализации значение этого объекта не изменится, и компилятор может использовать этот факт для оптимизации. Это также помогает запретить программисту писать код, который изменяет объекты, которые не должны были изменяться после инициализации.constexpr
объявляет объект пригодным для использования в том, что стандарт называет константными выражениями . Но учтите, чтоconstexpr
это не единственный способ сделать это.Применительно к функциям основное отличие заключается в следующем:
const
может использоваться только для нестатических функций-членов, но не для функций в целом. Это дает гарантию, что функция-член не изменяет ни один из нестатических элементов данных.constexpr
может использоваться как с членами, так и с не членами, а также с конструкторами. Он объявляет функцию пригодной для использования в константных выражениях . Компилятор примет его, только если функция соответствует определенным критериям (7.1.5 / 3,4), наиболее важно (†) :return
утверждений, допускается только один оператор. В случае конструктора разрешены только список инициализации, typedefs и static assert. (= default
и= delete
тоже разрешено.)asm
объявление,goto
оператор, оператор с меткой, отличной отcase
иdefault
, try-block, определение переменной не-литерала тип, определение переменной статической или длительности хранения потока, определение переменной, для которой не выполняется инициализация.Постоянные выражения
Как сказано выше,
constexpr
объявляет оба объекта, а также функции, пригодные для использования в константных выражениях. Постоянное выражение не просто константа:Он может использоваться в местах, где требуется оценка во время компиляции, например, параметры шаблона и спецификаторы размера массива:
Но обратите внимание:
Объявление чего-либо как
constexpr
не обязательно гарантирует, что оно будет оценено во время компиляции. Он может использоваться для таких целей, но может использоваться и в других местах, которые оцениваются также во время выполнения.Объект может быть пригоден для использования в константных выражениях без объявления
constexpr
. Пример:Это возможно, потому что
N
, будучи постоянным и инициализированным во время объявления с литералом, удовлетворяет критериям для постоянного выражения, даже если оно не объявленоconstexpr
.Так, когда я действительно должен использовать
constexpr
?Объект , как
N
выше , может быть использован в качестве постоянного выражения , не объявляютсяconstexpr
. Это верно для всех объектов, которые:const
[Это связано с §5.19 / 2: константное выражение не должно включать подвыражение, которое включает «модификацию lvalue-to-rvalue, если […] glvalue целочисленного или перечислимого типа […]» Спасибо Ричарду Смиту за исправление моего ранее утверждали, что это верно для всех литеральных типов.]
Чтобы функция была пригодна для использования в константных выражениях, она должна быть явно объявлена
constexpr
; недостаточно просто удовлетворять критериям для функций с постоянным выражением. Пример:Когда я могу / должен использовать оба,
const
иconstexpr
вместе?А. В объявлениях объекта. Это никогда не требуется, когда оба ключевых слова ссылаются на один и тот же объект, который должен быть объявлен.
constexpr
подразумеваетconst
.такой же как
Однако обратите внимание, что могут быть ситуации, когда каждое ключевое слово ссылается на разные части объявления:
Здесь
NP
объявляется как адрес-константа-выражение, то есть указатель, который сам является константным выражением. (Это возможно , когда адрес генерируется путем применения оператора адреса к / глобальному выражению постоянная статическим.) Здесь, какconstexpr
иconst
требуется:constexpr
всегда относится к выражению объявляются (здесьNP
), в то время какconst
относится кint
(это объявляет pointer- к сопзЬ). Удалениеconst
может сделать выражение недопустимым (потому что (a) указатель на неконстантный объект не может быть константным выражением, а (b)&N
фактически является указателем на константу).Б. В объявлениях функций-членов. В C ++ 11
constexpr
подразумеваетсяconst
, а в C ++ 14 и C ++ 17 это не так. Функция-член, объявленная в C ++ 11 какдолжен быть объявлен как
под C ++ 14, чтобы все еще быть пригодным для использования в качестве
const
функции.источник
constexpr
функцию с неконстантным выражением, например с обычной переменной, это совершенно законно, и функция будет использоваться, как и любая другая функция. Он не будет оцениваться во время компиляции (потому что не может). Возможно, вы думаете, что это очевидно - но если бы я сказал, что функция, объявленная какconstexpr
, всегда будет оцениваться во время компиляции, она может быть неверно интерпретирована.constexpr
объектах, а не функциях. Мне нравится думатьconstexpr
об объектах как о принудительной оценке значений времени компиляции, аconstexpr
о функциях - как о том, что функция может быть оценена во время компиляции или во время выполнения в зависимости от ситуации.mutable
также могут быть измененыconst
функциями-членами.const
применяется к переменным и предотвращает их изменение в вашем коде.constexpr
сообщает компилятору, что это выражение приводит к значению постоянной времени компиляции , поэтому его можно использовать в таких местах, как длина массива, присвоениеconst
переменных и т. д. Ссылка, предоставленная Oli, имеет много прекрасных примеров.По сути, они представляют собой две разные концепции и могут (и должны) использоваться вместе.
источник
обзор
const
гарантирует, что программа не изменяет значение объекта . Темconst
не менее, не гарантирует, какой тип инициализации объект подвергается.Рассматривать:
Функция
max()
просто возвращает буквальное значение. Однако, поскольку инициализатор является вызовом функции, онmx
подвергается инициализации во время выполнения. Следовательно, вы не можете использовать его как константное выражение :constexpr
это новое ключевое слово C ++ 11, которое избавляет вас от необходимости создавать макросы и жестко закодированные литералы. Это также гарантирует, при определенных условиях, что объекты подвергаются статической инициализации . Он контролирует время оценки выражения. Обеспечивая оценку его выражения во время компиляции ,constexpr
позволяет вам определять истинные константные выражения, которые имеют решающее значение для критичных ко времени приложений, системного программирования, шаблонов и, вообще говоря, в любом коде, который опирается на константы времени компиляции.Функции с постоянными выражениями
Функция с постоянным выражением - это объявленная функция
constexpr
. Его тело должно быть не виртуальным и состоять только из одного оператора return, кроме typedefs и static asserts. Его аргументы и возвращаемое значение должны иметь литеральные типы. Его можно использовать с аргументами без константных выражений, но когда это сделано, результат не является константным выражением.Функция постоянного выражения предназначена для замены макросов и жестко закодированных литералов без ущерба для производительности или безопасности типов.
Объекты с постоянным выражением
Объект с постоянным выражением является объявленным объектом
constexpr
. Он должен быть инициализирован постоянным выражением или значением, созданным конструктором с постоянным выражением с аргументами постоянного выражения.Объект константного выражения ведет себя так, как если бы он был объявлен
const
, за исключением того, что он требует инициализации перед использованием, а его инициализатор должен быть константным выражением. Следовательно, объект константного выражения всегда можно использовать как часть другого константного выражения.Константные выражения
Постоянное выражение-конструктор является конструктором объявлена
constexpr
. Он может иметь список инициализации члена, но его тело должно быть пустым, кроме typedefs и статических утверждений. Его аргументы должны иметь буквенные типы.Конструктор с постоянным выражением позволяет компилятору инициализировать объект во время компиляции, при условии, что все аргументы конструктора являются константными выражениями.
Подсказки из книги Скотта Мейерса « Эффективное современное С ++ » о
constexpr
:constexpr
объекты являются постоянными и инициализируются значениями, известными во время компиляции;constexpr
функции выдают результаты времени компиляции при вызове с аргументами, значения которых известны во время компиляции;constexpr
объекты и функции могут использоваться в более широком диапазоне контекстов, чем не-constexpr
объекты и функции;constexpr
является частью интерфейса объекта или функции.Источник: Использование constexpr для улучшения безопасности, производительности и инкапсуляции в C ++ .
источник
В соответствии с книгой Бьярна Страуструпа «Язык программирования C ++ 4-й редакции»
• const : примерно означает «Я обещаю не менять это значение» (§7.5). Это используется главным образом для указания интерфейсов, так что данные могут передаваться функциям, не опасаясь их изменения.
Компилятор выполняет обещание, данное const.
• constexpr : примерно означает «быть оцененным во время компиляции» (§10.4). Это используется в первую очередь для указания констант,
например:
Чтобы функция могла использоваться в константном выражении, то есть в выражении, которое будет оцениваться компилятором, должно быть определено constexpr .
Например:
Чтобы быть constexpr, функция должна быть довольно простой: просто оператор возврата, вычисляющий значение. Функция constexpr может использоваться для непостоянных аргументов, но когда это сделано, результат не является константным выражением. Мы разрешаем вызывать функцию constexpr с аргументами неконстантных выражений в контекстах, которые не требуют константных выражений, поэтому мы не должны определять по существу одну и ту же функцию дважды: один раз для константных выражений и один раз для переменных.
В некоторых местах константные выражения требуются правилами языка (например, границы массивов (§2.2.5, §7.3), метки case (§2.2.4, §9.4.2), некоторые аргументы шаблона (§25.2) и константы, объявленные с помощью constexpr). В других случаях оценка времени компиляции важна для производительности. Независимо от проблем производительности, понятие неизменности (объекта с неизменяемым состоянием) является важной проблемой проектирования (§10.4).
источник
И то,
const
и другоеconstexpr
можно применять к переменным и функциям. Хотя они похожи друг на друга, на самом деле это очень разные понятия.Оба
const
иconstexpr
означают, что их значения не могут быть изменены после их инициализации. Так, например:Принципиальным различием между
const
иconstexpr
является время, когда их значения инициализации известны (оценены). Хотя значенияconst
переменных могут быть оценены как во время компиляции, так и во время выполнения,constexpr
всегда оцениваются во время компиляции. Например:Основным преимуществом для того, чтобы знать, известно ли значение во время компиляции или во время выполнения, является тот факт, что константы времени компиляции могут использоваться всякий раз, когда необходимы константы времени компиляции. Например, C ++ не позволяет вам указывать C-массивы с переменной длиной.
Итак, это означает, что:
Таким образом,
const
переменные могут определять как константы времени компиляции, подобные тем,size1
которые можно использовать для указания размеров массива, так и константы времени выполнения, подобныеsize2
тем, которые известны только во время выполнения и не могут использоваться для определения размеров массива. С другой стороны,constexpr
всегда определяйте константы времени компиляции, которые могут указывать размеры массива.И то,
const
и другоеconstexpr
можно применить и к функциям.const
Функция должна быть функцией членом (метод, оператор) , где применениемconst
ключевого слова означает , что метод не может изменить значение их члена (не статический) полей. Например.А
constexpr
это другая концепция. Он отмечает функцию (член или не член) как функцию, которая может быть оценена во время компиляции, если в качестве аргументов переданы константы времени компиляции . Например, вы можете написать это.Кстати,
constexpr
функции - это обычные функции C ++, которые можно вызывать, даже если переданы непостоянные аргументы. Но в этом случае вы получаете значения не-constexpr.constexpr
Также может быть применено к функциям членов (методы), операторов и даже конструкторов. Например.Более «сумасшедший» образец.
источник
constexpr int
существует, но это пишетсяconst int
Как уже указывалось @ 0x499602d2,
const
только гарантирует, что значение не может быть изменено после инициализации, поскольку asconstexpr
(введено в C ++ 11) гарантирует, что переменная является постоянной времени компиляции.Рассмотрим следующий пример (из LearnCpp.com):
источник
Значение A
const int var
можно динамически установить в значение во время выполнения, и как только оно будет установлено в это значение, его уже нельзя будет изменить.A
constexpr int var
не может быть динамически установлен во время выполнения, а во время компиляции. И как только оно будет установлено на это значение, его уже нельзя будет изменить.Вот хороший пример:
Приведенный выше фрагмент прекрасно компилируется, и я прокомментировал те, которые приводят к ошибке.
Ключевые понятия, на которые следует обратить внимание, это понятия
compile time
иrun time
. В C ++ были введены новые нововведения, предназначенные для максимально возможного количества** know **
определенных вещей во время компиляции для улучшения производительности во время выполнения.источник
Я не думаю, что какой-либо из ответов действительно прояснит, какие именно побочные эффекты у него есть, или действительно, что это такое.
constexpr
иconst
в namespace / file-scope идентичны, когда инициализируются литералом или выражением; но с функцией,const
может быть инициализирован любой функцией, ноconstexpr
инициализирован non-constexpr (функция, которая не помечена как constexpr или выражение не constexpr), будет генерировать ошибку компилятора. И тоconstexpr
и другоеconst
неявно является внутренней связью для переменных (ну, на самом деле, они не доживут до стадии связывания, если компилируют -O1 и более сильные, иstatic
не заставляют компилятор испускать внутренний (локальный) символ компоновщика дляconst
илиconstexpr
когда в -O1 или более сильный, единственный раз, когда это происходит, если вы берете адрес переменной.const
Иconstexpr
будет внутренним символом, если не выражено с помощьюextern
ieextern constexpr/const int i = 3;
должен быть использован). В функцииconstexpr
делает функцию постоянно никогда не достигающей стадии связывания (независимо отextern
илиinline
в определении или -O0 или -Ofast), тогда какconst
никогда не достигает,static
иinline
только влияет на -O1 и выше. Когдаconst
/constexpr
переменная инициализируетсяconstexpr
функцией, загрузка всегда оптимизируется с любым флагом оптимизации, но она никогда не оптимизируется, если функция толькоstatic
илиinline
, или если переменная не являетсяconst
/constexpr
.Стандартная компиляция (-O0)
компилируется в
Однако
Компилируется в
Это ясно показывает, что
constexpr
инициализацияconst/constexpr
переменной file-scope происходит во время компиляции и не генерирует глобальный символ, тогда как ее использование не вызывает инициализацию раньшеmain
во время выполнения.Компиляция с использованием -Ofast
Даже -Ofast не оптимизирует нагрузку! https://godbolt.org/z/r-mhif , так что вам нужно
constexpr
constexpr
Функции также могут быть вызваны из другихconstexpr
функций для того же результата.constexpr
функция также предотвращает использование чего-либо, что не может быть сделано во время компиляции в функции; например, звонок<<
оператору наstd::cout
.constexpr
в области видимости блока ведет себя так же, что выдает ошибку, если инициализируется функцией non-constexpr; значение также подставляется сразу.В конце концов, его основное предназначение похоже на встроенную функцию C, но она эффективна только тогда, когда функция используется для инициализации переменных области файла (что функции не могут делать на C, но они могут на C ++, потому что она позволяет динамическую инициализацию file- переменные области видимости), за исключением того, что функция не может экспортировать глобальный / локальный символ также в компоновщик, даже используя
extern/static
, что вы могли бы использоватьinline
на C; Функции назначения переменных в области блока могут быть встроены просто с помощью оптимизации -O1 без использованияconstexpr
C и C ++.источник
Прежде всего, оба являются классификаторами в C ++. Объявленная переменная const должна быть инициализирована и не может быть изменена в будущем. Следовательно, обычно переменная, объявленная как const, будет иметь значение даже до компиляции.
Но для constexpr это немного другое.
Для constexpr вы можете указать выражение, которое можно было бы оценить во время компиляции программы.
Очевидно, что переменная, объявленная как constexper, не может быть изменена в будущем так же, как const.
источник