Peut-être que cet article de la boost/hanabibliothèque peut éclairer certains constexprproblèmes que vous pouvez utiliser constexpret où vous ne pouvez pas: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry
@ 0x499602D2 " signifie simplement que la valeur ne peut pas être modifiée " Pour un scalaire initialisé avec un littéral, une valeur qui ne peut pas être modifiée est également une constante de temps de compilation.
curiousguy
@curiousguy Ouais mon commentaire était très simplifié. Certes, j'étais nouveau à l' constexprépoque :)
0x499602D2
Réponses:
587
Signification et syntaxe de base
Les deux mots clés peuvent être utilisés dans la déclaration d'objets ainsi que dans les fonctions. La différence de base appliquée aux objets est la suivante:
constdéclare un objet comme constant . Cela implique une garantie qu'une fois initialisé, la valeur de cet objet ne changera pas, et le compilateur peut utiliser ce fait pour les optimisations. Cela aide également à empêcher le programmeur d'écrire du code qui modifie des objets qui n'étaient pas destinés à être modifiés après l'initialisation.
constexpr déclare un objet comme apte à être utilisé dans ce que la norme appelle des expressions constantes . Mais notez que ce constexprn'est pas la seule façon de procéder.
Lorsqu'il est appliqué à fonctions, la différence fondamentale est la suivante:
constne peut être utilisé que pour les fonctions membres non statiques, pas pour les fonctions en général. Il garantit que la fonction membre ne modifie aucun des membres de données non statiques.
constexprpeut être utilisé avec des fonctions membres et non membres, ainsi qu'avec des constructeurs. Il déclare la fonction apte à être utilisée dans des expressions constantes . Le compilateur ne l'acceptera que si la fonction répond à certains critères (7.1.5 / 3,4), surtout (†) :
Le corps de la fonction doit être non virtuel et extrêmement simple: à part les typedefs et les assertions statiques, un seul return instruction est autorisée. Dans le cas d'un constructeur, seules une liste d'initialisation, des typedefs et une assertion statique sont autorisés. ( = defaultet = deletesont également autorisés.)
Depuis C ++ 14, les règles sont plus détendues, ce qui est autorisé depuis lors à l'intérieur d'une fonction constexpr: asmdéclaration, ungoto instruction, une instruction avec une étiquette autre que caseet default, try-block, la définition d'une variable de non-littéral type, définition d'une variable de durée de stockage statique ou thread, définition d'une variable pour laquelle aucune initialisation n'est effectuée.
Les arguments et le type de retour doivent être des types littéraux (c'est-à-dire, en général, des types très simples, généralement des scalaires ou des agrégats)
Expressions constantes
Comme indiqué ci-dessus, constexprdéclare les deux objets ainsi que les fonctions comme aptes à être utilisés dans des expressions constantes. Une expression constante est plus qu'une simple constante:
Il peut être utilisé dans des endroits qui nécessitent une évaluation au moment de la compilation, par exemple, des paramètres de modèle et des spécificateurs de taille de tableau:
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
Mais notez:
Déclarer quelque chose comme constexprne garantit pas nécessairement qu'il sera évalué au moment de la compilation. Il peut être utilisé pour cela, mais il peut également être utilisé à d'autres endroits qui sont évalués au moment de l'exécution.
Un objet peut être apte à être utilisé dans des expressions constantes sans être déclaré constexpr. Exemple:
int main(){constint N =3;int numbers[N]={1,2,3};// N is constant expression}
Ceci est possible car N, étant constant et initialisé au moment de la déclaration avec un littéral, satisfait les critères d'une expression constante, même si elle n'est pas déclarée constexpr.
Alors, quand dois-je réellement utiliser constexpr?
Un objet comme Nci-dessus peut être utilisé comme expression constante sans être déclaré constexpr. Cela est vrai pour tous les objets qui sont:
const
de type intégral ou énumération et
initialisé au moment de la déclaration avec une expression qui est elle-même une expression constante
[Ceci est dû au §5.19 / 2: Une expression constante ne doit pas inclure une sous-expression qui implique "une modification de valeur en valeur à moins que […] une valeur de type entier ou énumération […]" Merci à Richard Smith d'avoir corrigé mon affirmaient plus tôt que cela était vrai pour tous les types littéraux.]
Pour qu'une fonction soit apte à être utilisée dans des expressions constantes, elle doit être explicitement déclarée constexpr; il ne suffit pas qu'il satisfasse simplement aux critères des fonctions d'expression constante. Exemple:
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}
Quand puis-je / dois-je utiliser les deux, constetconstexpr ensemble?
A. Dans les déclarations d'objet. Cela n'est jamais nécessaire lorsque les deux mots clés font référence au même objet à déclarer. constexprimplique const.
constexprconstint N =5;
est le même que
constexprint N =5;
Cependant, notez qu'il peut y avoir des situations où les mots clés font chacun référence à différentes parties de la déclaration:
staticconstexprint N =3;int main(){constexprconstint*NP =&N;}
Ici, NPest déclaré comme une expression constante d'adresse, c'est-à-dire un pointeur qui est lui-même une expression constante. (Ceci est possible lorsque l'adresse est générée en appliquant l'opérateur d'adresse à une expression constante statique / globale.) Ici, les deux constexpret constsont obligatoires: constexprfait toujours référence à l'expression déclarée (ici NP), tandis que constfait référence à int(il déclare un pointeur- to-const). La suppression du constrendrait l'expression illégale (car (a) un pointeur vers un objet non-const ne peut pas être une expression constante, et (b) &Nest en fait un pointeur vers une constante).
B. Dans les déclarations des fonctions membres. En C ++ 11, constexprimplique const, alors qu'en C ++ 14 et C ++ 17 ce n'est pas le cas. Une fonction membre déclarée sous C ++ 11 comme
constexprvoid f();
doit être déclaré comme
constexprvoid f()const;
sous C ++ 14 afin d'être toujours utilisable en tant que constfonction.
L'OMI "pas nécessairement évalué au moment de la compilation" est moins utile que de les considérer comme "évalués au moment de la compilation". Les contraintes sur une expression constante signifient qu'il serait relativement facile pour un compilateur de l'évaluer. Un compilateur doit se plaindre si ces contraintes ne sont pas satisfaites. Puisqu'il n'y a pas d'effets secondaires, vous ne pouvez jamais faire de différence si un compilateur l'a "évalué" ou non.
aschepler
10
@aschepler Bien sûr. Mon point principal est que si vous appelez une constexprfonction sur une expression non constante, par exemple une variable ordinaire, c'est parfaitement légal et la fonction sera utilisée comme n'importe quelle autre fonction. Il ne sera pas évalué au moment de la compilation (car il ne peut pas). Peut-être pensez-vous que c'est évident - mais si j'ai déclaré qu'une fonction déclarée comme étant constexprtoujours évaluée au moment de la compilation, elle pourrait être interprétée de la mauvaise façon.
jogojapan
5
Oui, je parlais d' constexprobjets, pas de fonctions. J'aime penser constexpraux objets comme forçant l'évaluation des valeurs au moment de la compilation, et constexpraux fonctions comme permettant à la fonction d'être évaluée au moment de la compilation ou au moment de l'exécution, selon le cas.
aschepler
2
Une correction: «const» est seulement une restriction que VOUS ne pouvez pas changer la valeur d'une variable; il ne fait aucune promesse que la valeur ne changera pas (c'est-à-dire par quelqu'un d'autre). C'est une propriété d'écriture, pas une propriété de lecture.
Jared Grubb
3
Cette phrase: elle garantit que la fonction membre ne modifie aucun des membres de données non statiques. manque un détail important. Les membres marqués comme mutablepouvant également être modifiés par constles fonctions membres.
Omnifarious
119
consts'applique aux variables et les empêche d'être modifiées dans votre code.
constexprindique au compilateur que cette expression se traduit par une valeur de constante de temps de compilation , de sorte qu'elle peut être utilisée dans des endroits comme des longueurs de tableau, des affectations à des constvariables, etc. Le lien donné par Oli a beaucoup d'excellents exemples.
Fondamentalement, ce sont deux concepts différents et peuvent (et devraient) être utilisés ensemble.
La fonction max()renvoie simplement une valeur littérale. Cependant, comme l'initialiseur est un appel de fonction, il mxsubit une initialisation d'exécution. Par conséquent, vous ne pouvez pas l'utiliser comme expression constante :
int arr[mx];// error: “constant expression required”
constexprest un nouveau mot clé C ++ 11 qui vous débarrasse de la nécessité de créer des macros et des littéraux codés en dur. Il garantit également, sous certaines conditions, que les objets subissent une initialisation statique . Il contrôle le temps d'évaluation d'une expression. En imposant une évaluation au moment de la compilation de son expression , constexprvous permet de définir de véritables expressions constantes qui sont cruciales pour les applications critiques, la programmation système, les modèles et, de manière générale, dans tout code qui repose sur des constantes au moment de la compilation.
Fonctions d'expression constante
Une fonction d'expression constante est une fonction déclarée constexpr. Son corps doit être non virtuel et se composer d'une seule instruction de retour uniquement, à l'exception des typedefs et des assertions statiques. Ses arguments et sa valeur de retour doivent avoir des types littéraux. Il peut être utilisé avec des arguments d'expression non constante, mais lorsque cela est fait, le résultat n'est pas une expression constante.
Une fonction d'expression constante est destinée à remplacer les macros et les littéraux codés en dur sans sacrifier les performances ou la sécurité des types.
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)
Objets à expression constante
Un objet à expression constante est un objet déclaré constexpr. Il doit être initialisé avec une expression constante ou une valeur r construite par un constructeur d'expression constante avec des arguments d'expression constante.
Un objet à expression constante se comporte comme s'il avait été déclaré const, sauf qu'il nécessite une initialisation avant utilisation et que son initialiseur doit être une expression constante. Par conséquent, un objet à expression constante peut toujours être utilisé dans le cadre d'une autre expression constante.
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
Constructeurs à expression constante
Un constructeur à expression constante est un constructeur déclaré constexpr. Il peut avoir une liste d'initialisation de membre mais son corps doit être vide, à l'exception des typedefs et des assertions statiques. Ses arguments doivent avoir des types littéraux.
Un constructeur à expression constante permet au compilateur d'initialiser l'objet au moment de la compilation, à condition que les arguments du constructeur soient tous des expressions constantes.
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
Conseils tirés du livre Effective Modern C ++ de Scott Meyers sur constexpr:
constexpr les objets sont const et sont initialisés avec des valeurs connues lors de la compilation;
constexpr les fonctions produisent des résultats au moment de la compilation lorsqu'elles sont appelées avec des arguments dont les valeurs sont connues pendant la compilation;
constexprles objets et les fonctions peuvent être utilisés dans un éventail de contextes plus large que les non- constexprobjets et les fonctions;
constexpr fait partie de l'interface d'un objet ou d'une fonction.
Merci pour le bon exemple de code montrant les différentes situations. Aussi grandes que soient certaines des autres explications, j'ai trouvé que voir le code en action était beaucoup plus utile et compréhensible. Cela m'a vraiment aidé à consolider ma compréhension de ce qui se passe.
RTHarston
35
D'après le livre de "The C ++ Programming Language 4th Editon" de Bjarne Stroustrup
• const : qui signifie à peu près '' Je promets de ne pas changer cette valeur '' (§7.5). Ceci est utilisé principalement pour spécifier les interfaces, afin que les données puissent être transmises aux fonctions sans craindre d'être modifiées.
Le compilateur applique la promesse faite par const.
• constexpr : signifiant à peu près '' à évaluer au moment de la compilation '' (§10.4). Ceci est utilisé principalement pour spécifier des constantes, pour permettre
Par exemple:
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
Pour qu'une fonction soit utilisable dans une expression constante, c'est-à-dire dans une expression qui sera évaluée par le compilateur, elle doit être définie constexpr . Par exemple:
constexprdouble square(double x){return x∗x;}
Pour être constexpr, une fonction doit être plutôt simple: juste une déclaration de retour calculant une valeur. Une fonction constexpr peut être utilisée pour des arguments non constants, mais lorsque cela est fait, le résultat n'est pas une expression constante. Nous permettons à une fonction constexpr d'être appelée avec des arguments d'expression non constante dans des contextes qui ne nécessitent pas d'expressions constantes, de sorte que nous n'avons pas à définir essentiellement la même fonction deux fois: une fois pour les expressions constantes et une fois pour les variables.
À quelques endroits, des expressions constantes sont requises par les règles de langage (par exemple, les limites de tableau (§2.2.5, §7.3), les étiquettes de casse (§2.2.4, §9.4.2), certains arguments de modèle (§25.2), et constantes déclarées à l'aide de constexpr). Dans d'autres cas, l'évaluation à la compilation est importante pour les performances. Indépendamment des problèmes de performances, la notion d'immuabilité (d'un objet avec un état immuable) est une préoccupation de conception importante (§10.4).
il y a encore des problèmes de performances. Semble que la fonction constexpr si elle est évaluée au moment de l'exécution peut être plus lente que la version non constexpr de la fonction. De plus, si nous avons une valeur constante, devrions-nous préférer "const" ou "constexpr"? (plus un assemblage généré par une question de style a la même apparence)
CoffeDeveloper
31
Les deux constet constexprpeuvent être appliqués aux variables et aux fonctions. Même s'ils se ressemblent, ce sont en fait des concepts très différents.
Les deux constet constexprsignifient que leurs valeurs ne peuvent pas être modifiées après leur initialisation. Ainsi, par exemple:
constint x1=10;constexprint x2=10;
x1=20;// ERROR. Variable 'x1' can't be changed.
x2=20;// ERROR. Variable 'x2' can't be changed.
La principale différence entre constet constexprest le moment où leurs valeurs d'initialisation sont connues (évaluées). Bien que les valeurs des constvariables puissent être évaluées au moment de la compilation et à l'exécution, elles constexprsont toujours évaluées au moment de la compilation. Par exemple:
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.
Le principal avantage de savoir si la valeur est connue au moment de la compilation ou à l'exécution est le fait que les constantes de temps de compilation peuvent être utilisées chaque fois que des constantes de temps de compilation sont nécessaires. Par exemple, C ++ ne vous permet pas de spécifier des tableaux C avec des longueurs variables.
int temp=rand();// temp is generated by the the random generator at runtime.int array1[10];// OK.int array2[temp];// ERROR.
Cela signifie donc que:
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.
Ainsi, les constvariables peuvent définir à la fois des constantes de temps de compilation comme size1celle-ci peuvent être utilisées pour spécifier des tailles de tableau et des constantes d'exécution comme size2celles-ci ne sont connues qu'au moment de l'exécution et ne peuvent pas être utilisées pour définir des tailles de tableau. D'un autre côté, constexprdéfinissez toujours des constantes de temps de compilation qui peuvent spécifier des tailles de tableau.
Les deux constet constexprpeuvent également être appliqués aux fonctions. Une constfonction doit être une fonction membre (méthode, opérateur) où l'application du constmot clé signifie que la méthode ne peut pas modifier les valeurs de leurs champs membres (non statiques). Par exemple.
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.}};
A constexprest un concept différent. Il marque une fonction (membre ou non-membre) comme la fonction qui peut être évaluée au moment de la compilation si des constantes de temps de compilation sont passées comme arguments . Par exemple, vous pouvez écrire ceci.
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.
Par ailleurs, les constexprfonctions sont les fonctions C ++ normales qui peuvent être appelées même si des arguments non constants sont passés. Mais dans ce cas, vous obtenez les valeurs non 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.
Le constexprpeut également être appliqué aux fonctions membres (méthodes), aux opérateurs et même aux constructeurs. Par exemple.
class test2
{staticconstexprint function(int value){return(value+1);}void f(){int x[function(10)];}};
Un échantillon plus «fou».
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.
Aussi, en C, constexpr intexiste mais il est orthographiéconst int
curiousguy
8
Comme @ 0x499602d2 l'a déjà souligné, constgarantit uniquement qu'une valeur ne peut pas être modifiée après l'initialisation où as constexpr(introduit en C ++ 11) garantit que la variable est une constante de temps de compilation.
Prenons l'exemple suivant (de 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 varpeut être défini dynamiquement sur une valeur au moment de l'exécution et une fois qu'il est défini sur cette valeur, il ne peut plus être modifié.
Un constexpr int varne peut pas être défini dynamiquement au moment de l'exécution, mais plutôt au moment de la compilation. Et une fois qu'il est réglé sur cette valeur, il ne peut plus être modifié.
Voici un exemple solide:
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}
L'extrait ci-dessus se compile bien et j'ai commenté ceux qui provoquent une erreur.
Les notions clés ici à prendre en compte sont les notions de compile timeet run time. De nouvelles innovations ont été introduites dans C ++ destinées à autant que possible ** know **certaines choses au moment de la compilation pour améliorer les performances à l'exécution.
Je ne pense pas que l'une des réponses précise vraiment quels effets secondaires elle a, ni même ce qu'elle est.
constexpret constà namespace / file-scope sont identiques lorsqu'ils sont initialisés avec un littéral ou une expression; mais avec une fonction, constpeut être initialisé par n'importe quelle fonction, mais constexprinitialisé par un non-constexpr (une fonction qui n'est pas marquée avec constexpr ou une expression non constexpr) générera une erreur de compilation. Les deux constexpret constsont implicitement un lien interne pour les variables (enfin, en fait, ils ne survivent pas pour atteindre l'étape de liaison si la compilation de -O1 et plus fort, et staticne force pas le compilateur à émettre un symbole de lien interne (local) pour constou constexprlorsqu'il est à -O1 ou plus fort, la seule fois où il le fait est que si vous prenez l'adresse de la variable. constet constexprsera un symbole interne , à moins exprimé avecextern -à- direextern constexpr/const int i = 3;doit être utilisé). Sur une fonction, constexprla fonction n'atteint jamais définitivement la phase de liaison (quelle que externsoit inlinela définition ou -O0 ou -Ofast), alors qu'elle constne le fait jamais, staticet inlinen'a cet effet que sur -O1 et au-dessus. Lorsqu'une variable const/ constexprest initialisée par une constexprfonction, la charge est toujours optimisée avec n'importe quel indicateur d'optimisation, mais elle n'est jamais optimisée si la fonction est uniquement staticou inline, ou si la variable n'est pas un const/ constexpr.
Compilation standard (-O0)
#include<iostream>constexprint multiply (int x,int y){return x * y;}externconstint val = multiply(10,10);int main (){
std::cout << val;}
compile en
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):...
toutefois
#include<iostream>constint multiply (int x,int y){return x * y;}constint val = multiply(10,10);//constexpr is an errorint main (){
std::cout << val;}
Compile vers
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
Cela montre clairement que constexprl'initialisation de la const/constexprvariable de portée de fichier se produit au moment de la compilation et ne produit aucun symbole global, tandis que son absence entraîne l'initialisation avant l' mainexécution.
constexprLes fonctions peuvent également être appelées à l'intérieur d'autres constexprfonctions pour le même résultat. constexprsur une fonction empêche également l'utilisation de tout ce qui ne peut pas être fait au moment de la compilation dans la fonction; par exemple, un appel à l' <<opérateur activé std::cout.
constexprat block scope se comporte de la même manière en ce qu'il produit une erreur s'il est initialisé par une fonction non constexpr; la valeur est également remplacée immédiatement.
En fin de compte, son objectif principal est comme la fonction en ligne de C, mais il n'est efficace que lorsque la fonction est utilisée pour initialiser des variables de portée de fichier (ce que les fonctions ne peuvent pas faire sur C, mais elles le peuvent sur C ++ car elle permet l'initialisation dynamique de fichier- variables de portée), sauf que la fonction ne peut pas exporter un symbole global / local également dans l'éditeur de liens, même en utilisant extern/staticce que vous pourriez faire avec inlineC; Les fonctions d'affectation de variables de portée de bloc peuvent être intégrées en utilisant simplement une optimisation -O1 sans constexprsur C et C ++.
Joli point sur l'éditeur de liens. Pourrait-il être considéré comme plus sûr en général d'utiliser constexpr car il en résulte moins de fuites de symboles?
Neil McGill
1
@NeilMcGill pas vraiment car en ligne et statique, le compilateur n'émettra pas de symbole local à multiplier s'il compile en utilisant -O1 ou plus fort. Constexpr est le seul qui optimise la charge pour val, mais à part cela, il est identique à mettre statique ou en ligne avant la fonction. J'ai aussi oublié autre chose. Constexpr est le seul mot-clé qui n'émet pas de symbole pour la fonction sur -O0, statique et en ligne
Lewis Kelsey
1
Tout d'abord, les deux sont des qualificatifs en c ++. Une variable déclarée const doit être initialisée et ne pourra plus être modifiée à l'avenir. Par conséquent, généralement une variable déclarée comme const aura une valeur avant même la compilation.
Mais, pour constexpr, c'est un peu différent.
Pour constexpr, vous pouvez donner une expression qui pourrait être évaluée lors de la compilation du programme.
De toute évidence, la variable déclarée comme constexper ne peut pas être modifiée à l'avenir tout comme const.
constexpr
crée une constante de compilation;const
signifie simplement que la valeur ne peut pas être modifiée.boost/hana
bibliothèque peut éclairer certainsconstexpr
problèmes que vous pouvez utiliserconstexpr
et où vous ne pouvez pas: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…constexpr
époque :)Réponses:
Signification et syntaxe de base
Les deux mots clés peuvent être utilisés dans la déclaration d'objets ainsi que dans les fonctions. La différence de base appliquée aux objets est la suivante:
const
déclare un objet comme constant . Cela implique une garantie qu'une fois initialisé, la valeur de cet objet ne changera pas, et le compilateur peut utiliser ce fait pour les optimisations. Cela aide également à empêcher le programmeur d'écrire du code qui modifie des objets qui n'étaient pas destinés à être modifiés après l'initialisation.constexpr
déclare un objet comme apte à être utilisé dans ce que la norme appelle des expressions constantes . Mais notez que ceconstexpr
n'est pas la seule façon de procéder.Lorsqu'il est appliqué à fonctions, la différence fondamentale est la suivante:
const
ne peut être utilisé que pour les fonctions membres non statiques, pas pour les fonctions en général. Il garantit que la fonction membre ne modifie aucun des membres de données non statiques.constexpr
peut être utilisé avec des fonctions membres et non membres, ainsi qu'avec des constructeurs. Il déclare la fonction apte à être utilisée dans des expressions constantes . Le compilateur ne l'acceptera que si la fonction répond à certains critères (7.1.5 / 3,4), surtout (†) :return
instruction est autorisée. Dans le cas d'un constructeur, seules une liste d'initialisation, des typedefs et une assertion statique sont autorisés. (= default
et= delete
sont également autorisés.)asm
déclaration, ungoto
instruction, une instruction avec une étiquette autre quecase
etdefault
, try-block, la définition d'une variable de non-littéral type, définition d'une variable de durée de stockage statique ou thread, définition d'une variable pour laquelle aucune initialisation n'est effectuée.Expressions constantes
Comme indiqué ci-dessus,
constexpr
déclare les deux objets ainsi que les fonctions comme aptes à être utilisés dans des expressions constantes. Une expression constante est plus qu'une simple constante:Il peut être utilisé dans des endroits qui nécessitent une évaluation au moment de la compilation, par exemple, des paramètres de modèle et des spécificateurs de taille de tableau:
Mais notez:
Déclarer quelque chose comme
constexpr
ne garantit pas nécessairement qu'il sera évalué au moment de la compilation. Il peut être utilisé pour cela, mais il peut également être utilisé à d'autres endroits qui sont évalués au moment de l'exécution.Un objet peut être apte à être utilisé dans des expressions constantes sans être déclaré
constexpr
. Exemple:Ceci est possible car
N
, étant constant et initialisé au moment de la déclaration avec un littéral, satisfait les critères d'une expression constante, même si elle n'est pas déclaréeconstexpr
.Alors, quand dois-je réellement utiliser
constexpr
?Un objet comme
N
ci-dessus peut être utilisé comme expression constante sans être déclaréconstexpr
. Cela est vrai pour tous les objets qui sont:const
[Ceci est dû au §5.19 / 2: Une expression constante ne doit pas inclure une sous-expression qui implique "une modification de valeur en valeur à moins que […] une valeur de type entier ou énumération […]" Merci à Richard Smith d'avoir corrigé mon affirmaient plus tôt que cela était vrai pour tous les types littéraux.]
Pour qu'une fonction soit apte à être utilisée dans des expressions constantes, elle doit être explicitement déclarée
constexpr
; il ne suffit pas qu'il satisfasse simplement aux critères des fonctions d'expression constante. Exemple:Quand puis-je / dois-je utiliser les deux,
const
etconstexpr
ensemble?A. Dans les déclarations d'objet. Cela n'est jamais nécessaire lorsque les deux mots clés font référence au même objet à déclarer.
constexpr
impliqueconst
.est le même que
Cependant, notez qu'il peut y avoir des situations où les mots clés font chacun référence à différentes parties de la déclaration:
Ici,
NP
est déclaré comme une expression constante d'adresse, c'est-à-dire un pointeur qui est lui-même une expression constante. (Ceci est possible lorsque l'adresse est générée en appliquant l'opérateur d'adresse à une expression constante statique / globale.) Ici, les deuxconstexpr
etconst
sont obligatoires:constexpr
fait toujours référence à l'expression déclarée (iciNP
), tandis queconst
fait référence àint
(il déclare un pointeur- to-const). La suppression duconst
rendrait l'expression illégale (car (a) un pointeur vers un objet non-const ne peut pas être une expression constante, et (b)&N
est en fait un pointeur vers une constante).B. Dans les déclarations des fonctions membres. En C ++ 11,
constexpr
impliqueconst
, alors qu'en C ++ 14 et C ++ 17 ce n'est pas le cas. Une fonction membre déclarée sous C ++ 11 commedoit être déclaré comme
sous C ++ 14 afin d'être toujours utilisable en tant que
const
fonction.la source
constexpr
fonction sur une expression non constante, par exemple une variable ordinaire, c'est parfaitement légal et la fonction sera utilisée comme n'importe quelle autre fonction. Il ne sera pas évalué au moment de la compilation (car il ne peut pas). Peut-être pensez-vous que c'est évident - mais si j'ai déclaré qu'une fonction déclarée comme étantconstexpr
toujours évaluée au moment de la compilation, elle pourrait être interprétée de la mauvaise façon.constexpr
objets, pas de fonctions. J'aime penserconstexpr
aux objets comme forçant l'évaluation des valeurs au moment de la compilation, etconstexpr
aux fonctions comme permettant à la fonction d'être évaluée au moment de la compilation ou au moment de l'exécution, selon le cas.mutable
pouvant également être modifiés parconst
les fonctions membres.const
s'applique aux variables et les empêche d'être modifiées dans votre code.constexpr
indique au compilateur que cette expression se traduit par une valeur de constante de temps de compilation , de sorte qu'elle peut être utilisée dans des endroits comme des longueurs de tableau, des affectations à desconst
variables, etc. Le lien donné par Oli a beaucoup d'excellents exemples.Fondamentalement, ce sont deux concepts différents et peuvent (et devraient) être utilisés ensemble.
la source
Aperçu
const
garantit qu'un programme ne change pas la valeur d'un objet . Cependant,const
ne garantit pas quel type d'initialisation l'objet subit.Considérer:
La fonction
max()
renvoie simplement une valeur littérale. Cependant, comme l'initialiseur est un appel de fonction, ilmx
subit une initialisation d'exécution. Par conséquent, vous ne pouvez pas l'utiliser comme expression constante :constexpr
est un nouveau mot clé C ++ 11 qui vous débarrasse de la nécessité de créer des macros et des littéraux codés en dur. Il garantit également, sous certaines conditions, que les objets subissent une initialisation statique . Il contrôle le temps d'évaluation d'une expression. En imposant une évaluation au moment de la compilation de son expression ,constexpr
vous permet de définir de véritables expressions constantes qui sont cruciales pour les applications critiques, la programmation système, les modèles et, de manière générale, dans tout code qui repose sur des constantes au moment de la compilation.Fonctions d'expression constante
Une fonction d'expression constante est une fonction déclarée
constexpr
. Son corps doit être non virtuel et se composer d'une seule instruction de retour uniquement, à l'exception des typedefs et des assertions statiques. Ses arguments et sa valeur de retour doivent avoir des types littéraux. Il peut être utilisé avec des arguments d'expression non constante, mais lorsque cela est fait, le résultat n'est pas une expression constante.Une fonction d'expression constante est destinée à remplacer les macros et les littéraux codés en dur sans sacrifier les performances ou la sécurité des types.
Objets à expression constante
Un objet à expression constante est un objet déclaré
constexpr
. Il doit être initialisé avec une expression constante ou une valeur r construite par un constructeur d'expression constante avec des arguments d'expression constante.Un objet à expression constante se comporte comme s'il avait été déclaré
const
, sauf qu'il nécessite une initialisation avant utilisation et que son initialiseur doit être une expression constante. Par conséquent, un objet à expression constante peut toujours être utilisé dans le cadre d'une autre expression constante.Constructeurs à expression constante
Un constructeur à expression constante est un constructeur déclaré
constexpr
. Il peut avoir une liste d'initialisation de membre mais son corps doit être vide, à l'exception des typedefs et des assertions statiques. Ses arguments doivent avoir des types littéraux.Un constructeur à expression constante permet au compilateur d'initialiser l'objet au moment de la compilation, à condition que les arguments du constructeur soient tous des expressions constantes.
Conseils tirés du livre Effective Modern C ++ de Scott Meyers sur
constexpr
:constexpr
les objets sont const et sont initialisés avec des valeurs connues lors de la compilation;constexpr
les fonctions produisent des résultats au moment de la compilation lorsqu'elles sont appelées avec des arguments dont les valeurs sont connues pendant la compilation;constexpr
les objets et les fonctions peuvent être utilisés dans un éventail de contextes plus large que les non-constexpr
objets et les fonctions;constexpr
fait partie de l'interface d'un objet ou d'une fonction.Source: Utilisation de constexpr pour améliorer la sécurité, les performances et l'encapsulation en C ++ .
la source
D'après le livre de "The C ++ Programming Language 4th Editon" de Bjarne Stroustrup
• const : qui signifie à peu près '' Je promets de ne pas changer cette valeur '' (§7.5). Ceci est utilisé principalement pour spécifier les interfaces, afin que les données puissent être transmises aux fonctions sans craindre d'être modifiées.
Le compilateur applique la promesse faite par const.
• constexpr : signifiant à peu près '' à évaluer au moment de la compilation '' (§10.4). Ceci est utilisé principalement pour spécifier des constantes, pour permettre
Par exemple:
Pour qu'une fonction soit utilisable dans une expression constante, c'est-à-dire dans une expression qui sera évaluée par le compilateur, elle doit être définie constexpr .
Par exemple:
Pour être constexpr, une fonction doit être plutôt simple: juste une déclaration de retour calculant une valeur. Une fonction constexpr peut être utilisée pour des arguments non constants, mais lorsque cela est fait, le résultat n'est pas une expression constante. Nous permettons à une fonction constexpr d'être appelée avec des arguments d'expression non constante dans des contextes qui ne nécessitent pas d'expressions constantes, de sorte que nous n'avons pas à définir essentiellement la même fonction deux fois: une fois pour les expressions constantes et une fois pour les variables.
À quelques endroits, des expressions constantes sont requises par les règles de langage (par exemple, les limites de tableau (§2.2.5, §7.3), les étiquettes de casse (§2.2.4, §9.4.2), certains arguments de modèle (§25.2), et constantes déclarées à l'aide de constexpr). Dans d'autres cas, l'évaluation à la compilation est importante pour les performances. Indépendamment des problèmes de performances, la notion d'immuabilité (d'un objet avec un état immuable) est une préoccupation de conception importante (§10.4).
la source
Les deux
const
etconstexpr
peuvent être appliqués aux variables et aux fonctions. Même s'ils se ressemblent, ce sont en fait des concepts très différents.Les deux
const
etconstexpr
signifient que leurs valeurs ne peuvent pas être modifiées après leur initialisation. Ainsi, par exemple:La principale différence entre
const
etconstexpr
est le moment où leurs valeurs d'initialisation sont connues (évaluées). Bien que les valeurs desconst
variables puissent être évaluées au moment de la compilation et à l'exécution, ellesconstexpr
sont toujours évaluées au moment de la compilation. Par exemple:Le principal avantage de savoir si la valeur est connue au moment de la compilation ou à l'exécution est le fait que les constantes de temps de compilation peuvent être utilisées chaque fois que des constantes de temps de compilation sont nécessaires. Par exemple, C ++ ne vous permet pas de spécifier des tableaux C avec des longueurs variables.
Cela signifie donc que:
Ainsi, les
const
variables peuvent définir à la fois des constantes de temps de compilation commesize1
celle-ci peuvent être utilisées pour spécifier des tailles de tableau et des constantes d'exécution commesize2
celles-ci ne sont connues qu'au moment de l'exécution et ne peuvent pas être utilisées pour définir des tailles de tableau. D'un autre côté,constexpr
définissez toujours des constantes de temps de compilation qui peuvent spécifier des tailles de tableau.Les deux
const
etconstexpr
peuvent également être appliqués aux fonctions. Uneconst
fonction doit être une fonction membre (méthode, opérateur) où l'application duconst
mot clé signifie que la méthode ne peut pas modifier les valeurs de leurs champs membres (non statiques). Par exemple.A
constexpr
est un concept différent. Il marque une fonction (membre ou non-membre) comme la fonction qui peut être évaluée au moment de la compilation si des constantes de temps de compilation sont passées comme arguments . Par exemple, vous pouvez écrire ceci.Par ailleurs, les
constexpr
fonctions sont les fonctions C ++ normales qui peuvent être appelées même si des arguments non constants sont passés. Mais dans ce cas, vous obtenez les valeurs non constexpr.Le
constexpr
peut également être appliqué aux fonctions membres (méthodes), aux opérateurs et même aux constructeurs. Par exemple.Un échantillon plus «fou».
la source
constexpr int
existe mais il est orthographiéconst int
Comme @ 0x499602d2 l'a déjà souligné,
const
garantit uniquement qu'une valeur ne peut pas être modifiée après l'initialisation où asconstexpr
(introduit en C ++ 11) garantit que la variable est une constante de temps de compilation.Prenons l'exemple suivant (de LearnCpp.com):
la source
A
const int var
peut être défini dynamiquement sur une valeur au moment de l'exécution et une fois qu'il est défini sur cette valeur, il ne peut plus être modifié.Un
constexpr int var
ne peut pas être défini dynamiquement au moment de l'exécution, mais plutôt au moment de la compilation. Et une fois qu'il est réglé sur cette valeur, il ne peut plus être modifié.Voici un exemple solide:
L'extrait ci-dessus se compile bien et j'ai commenté ceux qui provoquent une erreur.
Les notions clés ici à prendre en compte sont les notions de
compile time
etrun time
. De nouvelles innovations ont été introduites dans C ++ destinées à autant que possible** know **
certaines choses au moment de la compilation pour améliorer les performances à l'exécution.la source
Je ne pense pas que l'une des réponses précise vraiment quels effets secondaires elle a, ni même ce qu'elle est.
constexpr
etconst
à namespace / file-scope sont identiques lorsqu'ils sont initialisés avec un littéral ou une expression; mais avec une fonction,const
peut être initialisé par n'importe quelle fonction, maisconstexpr
initialisé par un non-constexpr (une fonction qui n'est pas marquée avec constexpr ou une expression non constexpr) générera une erreur de compilation. Les deuxconstexpr
etconst
sont implicitement un lien interne pour les variables (enfin, en fait, ils ne survivent pas pour atteindre l'étape de liaison si la compilation de -O1 et plus fort, etstatic
ne force pas le compilateur à émettre un symbole de lien interne (local) pourconst
ouconstexpr
lorsqu'il est à -O1 ou plus fort, la seule fois où il le fait est que si vous prenez l'adresse de la variable.const
etconstexpr
sera un symbole interne , à moins exprimé avecextern
-à- direextern constexpr/const int i = 3;
doit être utilisé). Sur une fonction,constexpr
la fonction n'atteint jamais définitivement la phase de liaison (quelle queextern
soitinline
la définition ou -O0 ou -Ofast), alors qu'elleconst
ne le fait jamais,static
etinline
n'a cet effet que sur -O1 et au-dessus. Lorsqu'une variableconst
/constexpr
est initialisée par uneconstexpr
fonction, la charge est toujours optimisée avec n'importe quel indicateur d'optimisation, mais elle n'est jamais optimisée si la fonction est uniquementstatic
ouinline
, ou si la variable n'est pas unconst
/constexpr
.Compilation standard (-O0)
compile en
toutefois
Compile vers
Cela montre clairement que
constexpr
l'initialisation de laconst/constexpr
variable de portée de fichier se produit au moment de la compilation et ne produit aucun symbole global, tandis que son absence entraîne l'initialisation avant l'main
exécution.Compilation à l'aide de -Ofast
Même -Ofast n'optimise pas la charge! https://godbolt.org/z/r-mhif , vous avez donc besoin
constexpr
constexpr
Les fonctions peuvent également être appelées à l'intérieur d'autresconstexpr
fonctions pour le même résultat.constexpr
sur une fonction empêche également l'utilisation de tout ce qui ne peut pas être fait au moment de la compilation dans la fonction; par exemple, un appel à l'<<
opérateur activéstd::cout
.constexpr
at block scope se comporte de la même manière en ce qu'il produit une erreur s'il est initialisé par une fonction non constexpr; la valeur est également remplacée immédiatement.En fin de compte, son objectif principal est comme la fonction en ligne de C, mais il n'est efficace que lorsque la fonction est utilisée pour initialiser des variables de portée de fichier (ce que les fonctions ne peuvent pas faire sur C, mais elles le peuvent sur C ++ car elle permet l'initialisation dynamique de fichier- variables de portée), sauf que la fonction ne peut pas exporter un symbole global / local également dans l'éditeur de liens, même en utilisant
extern/static
ce que vous pourriez faire avecinline
C; Les fonctions d'affectation de variables de portée de bloc peuvent être intégrées en utilisant simplement une optimisation -O1 sansconstexpr
sur C et C ++.la source
Tout d'abord, les deux sont des qualificatifs en c ++. Une variable déclarée const doit être initialisée et ne pourra plus être modifiée à l'avenir. Par conséquent, généralement une variable déclarée comme const aura une valeur avant même la compilation.
Mais, pour constexpr, c'est un peu différent.
Pour constexpr, vous pouvez donner une expression qui pourrait être évaluée lors de la compilation du programme.
De toute évidence, la variable déclarée comme constexper ne peut pas être modifiée à l'avenir tout comme const.
la source