Différence entre `constexpr` et` const`


593

Quelle est la différence entre constexpret const?

  • Quand puis-je utiliser un seul d'entre eux?
  • Quand puis-je utiliser les deux et comment en choisir un?

71
constexprcrée une constante de compilation; constsignifie simplement que la valeur ne peut pas être modifiée.
0x499602D2


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 expression
    
    int 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()
      {
        const int 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>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<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.

constexpr const int N = 5;

est le même que

constexpr int 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:

static constexpr int N = 3;

int main()
{
  constexpr const int *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

constexpr void f();

doit être déclaré comme

constexpr void f() const;

sous C ++ 14 afin d'être toujours utilisable en tant que constfonction.


3
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.



64

Aperçu

  • constgarantit qu'un programme ne change pas la valeur d'un objet . Cependant, constne garantit pas quel type d'initialisation l'objet subit.

    Considérer:

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

    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.

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)

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
{
    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

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 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

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.

Source: Utilisation de constexpr pour améliorer la sécurité, les performances et l'encapsulation en C ++ .


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:

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.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> 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

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:

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


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:

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.

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.

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.

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:

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.

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.

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.

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.
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.

Le constexprpeut également être appliqué aux fonctions membres (méthodes), aux opérateurs et même aux constructeurs. Par exemple.

class test2
{
    static constexpr int 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.
    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.

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;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime

5

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[]) {
    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
}

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.


3

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>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

compile en

val:
        .long   100  //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>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int 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.

Compilation à l'aide de -Ofast

Même -Ofast n'optimise pas la charge! https://godbolt.org/z/r-mhif , vous avez donc besoin constexpr


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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.