web-dev-qa-db-fra.com

constexpr vs statique const: Lequel préférer?

Pour définir des constantes à la compilation de types intégraux tels que ceux-ci (au niveau de la fonction et de la classe), quelle syntaxe est la meilleure?

static const int kMagic = 64; // (1)
constexpr int kMagic = 64;    // (2)

(1) fonctionne également pour les compilateurs C++ 98/03, à la place (2) nécessite au moins C++ 11. Y a-t-il d'autres différences entre les deux? Faut-il privilégier l’un ou l’autre dans le code C++ moderne et pourquoi?


[~ # ~] éditer [~ # ~]

J'ai essayé cet exemple de code avec CE de Godbolt :

int main()
{
#define USE_STATIC_CONST
#ifdef USE_STATIC_CONST
  static const int kOk = 0;
  static const int kError = 1;
#else
  constexpr int kOk = 0;
  constexpr int kError = 1;
#endif
  return kOk;
}

et pour le static const cas, il s’agit de l’assemblée générée par GCC 6.2:

main::kOk:
        .zero   4
main::kError:
        .long   1
main:
        Push    rbp
        mov     rbp, rsp
        mov     eax, 0
        pop     rbp
        ret

Par contre, pour constexpr c'est:

main:
        Push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 0
        mov     DWORD PTR [rbp-8], 1
        mov     eax, 0
        pop     rbp
        ret

Bien qu'à -O3 dans les deux cas, je reçois le même assemblage (optimisé):

main:
        xor     eax, eax
        ret

EDIT # 2

J'ai essayé ce code simple (en direct sur Ideone) :

#include <iostream>
using namespace std;

int main() {
    const int k1 = 10;
    constexpr int k2 = 2*k1;
    cout << k2 << '\n';
    return 0;
}

ce qui montre que const int k1 est évalué à compile-time, car il est utilisé pour calculer constexpr int k2.

Cependant, il semble y avoir un comportement différent pour doubles. J'ai créé une question distincte pour cela ici .

36
Mr.C64

Tant que nous parlons de la déclaration des constantes de compilation au moment de la compilation, scalaire types entiers ou enum, il n'y a absolument aucune différence entre utiliser const (static const dans la classe) ou constexpr.

Notez que les compilateurs sont nécessaires pour supporter static const int objets (déclarés avec des initialiseurs constants) dans des expressions constantes, ce qui signifie qu'ils n'ont pas d'autre choix que de traiter de tels objets en tant que constantes à la compilation. En outre, tant que ces objets restent inutilisés, ils ne nécessitent aucune définition, ce qui montre qu'ils ne seront pas utilisés comme valeurs d'exécution.

En outre, les règles de initialisation constante empêchent les appels locaux static const int les objets ne doivent pas être initialisés de manière dynamique, ce qui signifie que la déclaration de tels objets localement ne sera pas pénalisée. De plus, l'immunité des objets static intégraux aux problèmes d'ordre d'initialisation statique est une caractéristique très importante du langage.

constexpr est une extension et une généralisation du concept implémenté à l'origine en C++ via const avec un initialiseur constant. Pour les types entiers, constexpr n'offre rien de plus que ce que const avait déjà fait. constexpr effectue simplement une vérification précoce de la "constance" de l'initialiseur. Cependant, on pourrait dire que constexpr est une fonctionnalité spécialement conçue à cet effet, de sorte qu’elle s’intègre mieux sur le plan stylistique.

17
AnT

Il est garanti que la variable constexpr aura une valeur disponible au moment de la compilation. tandis que static const membres ou const variable peut signifier une valeur de compilation ou une valeur d’exécution. Taper constexpr exprime votre intention d'une valeur de compilation d'une manière beaucoup plus explicite que const.

Une dernière chose, en C++ 17, constexpr, les variables de données statiques seront également intégrées. Cela signifie que vous pouvez omettre la définition hors ligne de static constexpr variables, mais pas static const.


En guise de demande dans la section commentaire, voici une explication plus détaillée sur static const dans la portée de la fonction.

UNE static const variable à la portée de la fonction est à peu près la même chose, mais au lieu d’avoir une durée de stockage automatique, elle a une durée de stockage statique. Cela signifie en quelque sorte que l'équivalent de déclarer la variable comme étant globale, mais uniquement accessible dans la fonction.

Il est vrai qu’une variable static est initialisée au premier appel de la fonction, mais comme il s’agit de const, le compilateur essaiera d’aligner la valeur et d’optimiser complètement la variable. Ainsi, dans une fonction, if, la valeur est connue au moment de la compilation pour cette variable particulière, le compilateur l'optimisera probablement.

Cependant, si la valeur n'est pas connue au moment de la compilation pour un static const à la portée de la fonction, il pourrait silencieusement ralentir votre fonction (un tout petit peu), car il doit initialiser la valeur à runtime la première fois que la fonction est appelée. De plus, il doit vérifier si la valeur est initialisée à chaque appel de la fonction.

C'est l'avantage d'une variable constexpr. Si la valeur n'est pas connue au moment de la compilation, il s'agit d'une erreur de compilation et non d'une fonction plus lente. Ensuite, si vous n’avez aucun moyen de déterminer la valeur de votre variable au moment de la compilation, le compilateur vous le dira et vous pourrez faire quelque chose.

32
Guillaume Racicot