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 double
s. J'ai créé une question distincte pour cela ici .
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.
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.