Recherche…
Introduction
constexpr
est un mot - clé qui peut être utilisé pour marquer la valeur d'une variable en tant qu'expression constante, une fonction potentiellement utilisable dans les expressions constantes ou (depuis C ++ 17) une instruction if n'ayant qu'une seule de ses branches sélectionnée pour être compilée.
Remarques
Le mot-clé constexpr
été ajouté en C ++ 11 mais depuis quelques années depuis la publication du standard C ++ 11, tous les compilateurs principaux ne l'ont pas supporté. au moment de la publication du standard C ++ 11. Au moment de la publication de C ++ 14, tous les principaux compilateurs prennent en charge constexpr
.
variables constexpr
Une variable déclarée constexpr
est implicitement const
et sa valeur peut être utilisée comme une expression constante.
Comparaison avec #define
Un constexpr
est un remplacement de type sécurisé pour les expressions de compilation basées sur #define
. Avec constexpr
l'expression évaluée à la compilation est remplacée par le résultat. Par exemple:
int main()
{
constexpr int N = 10 + 2;
cout << N;
}
produira le code suivant:
cout << 12;
Une macro de compilation basée sur un pré-processeur serait différente. Considérer:
#define N 10 + 2
int main()
{
cout << N;
}
produira:
cout << 10 + 2;
qui sera évidemment converti en cout << 10 + 2;
. Cependant, le compilateur devrait faire plus de travail. En outre, cela crée un problème s'il n'est pas utilisé correctement.
Par exemple (avec #define
):
cout << N * 2;
formes:
cout << 10 + 2 * 2; // 14
Mais un constexpr
pré-évalué donnerait correctement 24
.
Comparaison avec const
Une variable const
est une variable qui a besoin de mémoire pour son stockage. Un constexpr
ne le fait pas. Un constexpr
produit une constante de temps de compilation, qui ne peut pas être modifiée. Vous pouvez soutenir que const
peut également ne pas être modifié. Mais considérez:
int main()
{
const int size1 = 10;
const int size2 = abs(10);
int arr_one[size1];
int arr_two[size2];
}
Avec la plupart des compilateurs, la deuxième instruction échouera (peut fonctionner avec GCC, par exemple). La taille de tout tableau, comme vous le savez peut-être, doit être une expression constante (c’est-à-dire qu’il en résulte une valeur à la compilation). La deuxième variable size2
se voit attribuer une valeur qui est décidée à l'exécution (même si vous savez qu'il s'agit de 10
, pour le compilateur, ce n'est pas la compilation).
Cela signifie qu'un const
peut ou peut ne pas être une constante constante de compilation. Vous ne pouvez pas garantir ou imposer qu'une valeur de const
particulière est absolument à la compilation. Vous pouvez utiliser #define
mais il a ses propres pièges.
Par conséquent, utilisez simplement:
int main()
{
constexpr int size = 10;
int arr[size];
}
Une expression constexpr
doit donner une valeur à la compilation. Ainsi, vous ne pouvez pas utiliser:
constexpr int size = abs(10);
Sauf si la fonction ( abs
) renvoie elle-même un constexpr
.
Tous les types de base peuvent être initialisés avec constexpr
.
constexpr bool FailFatal = true;
constexpr float PI = 3.14f;
constexpr char* site= "StackOverflow";
Fait intéressant, et commodément, vous pouvez également utiliser auto
:
constexpr auto domain = ".COM"; // const char * const domain = ".COM"
constexpr auto PI = 3.14; // constexpr double
fonctions constexpr
Une fonction déclarée constexpr
est implicitement constexpr
et les appels à une telle fonction peuvent générer des expressions constantes. Par exemple, la fonction suivante, si elle est appelée avec des arguments d'expression constante, produit également une expression constante:
constexpr int Sum(int a, int b)
{
return a + b;
}
Ainsi, le résultat de l'appel de fonction peut être utilisé en tant que tableau lié ou argument de modèle, ou pour initialiser une variable constexpr
:
int main()
{
constexpr int S = Sum(10,20);
int Array[S];
int Array2[Sum(20,30)]; // 50 array size, compile time
}
Notez que si vous supprimez constexpr
de la spécification de type de retour de la fonction, l'affectation à S
ne fonctionnera pas, car S
est une variable constexpr
et doit être constexpr
à un const de compilation. De même, la taille du tableau ne sera pas non plus une expression constante, si la fonction Sum
n'est pas constexpr
.
Ce qui est intéressant constexpr
fonctions constexpr
, c'est que vous pouvez aussi l'utiliser comme des fonctions ordinaires:
int a = 20;
auto sum = Sum(a, abs(-20));
Sum
ne sera plus une fonction constexpr
, elle sera compilée en tant que fonction ordinaire, en prenant des arguments variables (non constants) et en renvoyant une valeur non constante. Vous n'avez pas besoin d'écrire deux fonctions.
Cela signifie également que si vous essayez d'attribuer un tel appel à une variable non-const, il ne sera pas compilé:
int a = 20;
constexpr auto sum = Sum(a, abs(-20));
La raison en est simple: une constexpr
doit être attribuée à constexpr
. Cependant, l'appel de fonction ci-dessus fait de Sum
un non- constexpr
(la valeur R est non-const, mais la valeur L se déclare constexpr
).
La fonction constexpr
doit également renvoyer une constante à la compilation. Ce qui suit ne compilera pas:
constexpr int Sum(int a, int b)
{
int a1 = a; // ERROR
return a + b;
}
Parce que a1
est une variable non-constexpr et interdit à la fonction d'être une véritable fonction constexpr
. Le rendre constexpr
et lui assigner a
va également ne pas fonctionner - puisque la valeur d' a
paramètre (paramètre entrant) n'est toujours pas connue:
constexpr int Sum(int a, int b)
{
constexpr int a1 = a; // ERROR
..
De plus, la suite ne compilera pas non plus:
constexpr int Sum(int a, int b)
{
return abs(a) + b; // or abs(a) + abs(b)
}
Puisque abs(a)
n'est pas une expression constante (même abs(10)
ne fonctionnera pas, puisque abs
ne renvoie pas de constexpr int
!
Et ça?
constexpr int Abs(int v)
{
return v >= 0 ? v : -v;
}
constexpr int Sum(int a, int b)
{
return Abs(a) + b;
}
Nous avons conçu notre propre fonction Abs
qui est un constexpr
, et le corps d' Abs
ne constexpr
pas non plus la règle. En outre, sur le site d’appel (à l’intérieur de la Sum
), l’expression donne lieu à une constexpr
. Par conséquent, l'appel à la Sum(-10, 20)
sera une expression constante à la compilation qui donnera 30
.
Static si déclaration
L' if constexpr
peut être utilisée pour compiler de manière conditionnelle du code. La condition doit être une expression constante. La branche non sélectionnée est supprimée. Une instruction ignorée à l'intérieur d'un modèle n'est pas instanciée. Par exemple:
template<class T, class ... Rest>
void g(T &&p, Rest &&...rs)
{
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list
}
De plus, les variables et les fonctions qui ne sont utilisées que dans des instructions ignorées ne sont pas obligatoirement définies et les instructions de return
ignorées ne sont pas utilisées pour la déduction de type retour de fonction.
if constexpr
est distinct de #ifdef
. #ifdef
compile conditionnellement le code, mais uniquement sur la base de conditions pouvant être évaluées au moment du prétraitement. Par exemple, #ifdef
n'a pas pu être utilisé pour compiler conditionnellement du code en fonction de la valeur d'un paramètre de modèle. Par contre, if constexpr
ne peut pas être utilisé pour écarter un code syntaxiquement invalide, alors #ifdef
peut.
if constexpr(false) {
foobar; // error; foobar has not been declared
std::vector<int> v("hello, world"); // error; no matching constructor
}