Namespaces
Variants

constexpr specifier (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Table des matières

Explication

Le spécificateur constexpr déclare qu'il est possible d'évaluer la valeur des entités au moment de la compilation. Ces entités peuvent ensuite être utilisées là où seules les expressions constantes de compilation sont autorisées (à condition que des arguments de fonction appropriés soient fournis).

Un spécificateur constexpr utilisé dans une déclaration d'objet ou de fonction membre non statique (jusqu'en C++14) implique const .

Un spécificateur constexpr utilisé dans la première déclaration d'une fonction ou d'un membre de données statique (depuis C++17) implique inline . Si une déclaration d'une fonction ou d'un modèle de fonction possède un spécificateur constexpr , alors chaque déclaration doit contenir ce spécificateur.

constexpr variable

Une variable ou un modèle de variable (depuis C++14) peut être déclarée constexpr si toutes les conditions suivantes sont satisfaites :

(jusqu'en C++26)
(depuis C++26)

  • Il a une destruction constante, ce qui signifie que l'une des conditions suivantes doit être satisfaite :
  • Il n'est pas de type classe ni un tableau (éventuellement multidimensionnel) de celui-ci.
  • Il est d'un type classe avec un constexpr destructeur ou un tableau (éventuellement multidimensionnel) de celui-ci, et pour une expression hypothétique e dont le seul effet est de détruire l'objet, e serait une expression constante de base si la durée de vie de l'objet et de ses sous-objets non mutables (mais pas de ses sous-objets mutables) était considérée comme commençant dans e .

Si une variable constexpr n'est pas locale à l'unité de traduction , elle ne doit pas être initialisée pour référencer une entité locale à l'unité de traduction utilisable dans des expressions constantes, ni avoir un sous-objet qui référence une telle entité. Une telle initialisation est interdite dans une unité d'interface de module (en dehors de son fragment de module privé , s'il existe) ou dans une partition de module, et est dépréciée dans tout autre contexte.

(depuis C++20)

constexpr fonction

Une fonction ou un modèle de fonction peut être déclaré constexpr .

Une fonction est constexpr-suitable si toutes les conditions suivantes sont satisfaites :

(jusqu'en C++20)
  • Son type de retour (s'il existe) est un type littéral .
  • Chacun de ses types de paramètres est un type littéral.
(jusqu'en C++23)
(depuis C++20)
  • Son corps de fonction est = default , = delete , ou une instruction composée englobant uniquement les éléments suivants :
(jusqu'en C++14)
  • Son corps de fonction est = default , = delete , ou une instruction composée qui (jusqu'en C++20) ne contient pas les éléments suivants :
(jusqu'en C++20)
  • définitions de variables de types non littéraux
  • définitions de variables de durée de stockage statique ou thread
(depuis C++14)
(jusqu'en C++23)

Sauf pour les fonctions constexpr instanciées, les fonctions non-templatisées constexpr doivent être adaptées pour constexpr.

Pour une fonction constexpr non-constructrice qui n'est ni par défaut ni templatée, s'il n'existe aucune valeur d'argument telle qu'un appel de la fonction puisse être une sous-expression évaluée d'une expression constante de base , le programme est mal formé, aucun diagnostic requis.

Pour une fonction templatée constexpr , si aucune spécialisation de la fonction/du modèle de classe ne rendrait la fonction templatée adaptée à constexpr lorsqu'elle est considérée comme une fonction non-templatée, le programme est mal formé, aucun diagnostic requis.

(until C++23)

Un appel d'une fonction constexpr dans un contexte donné produit le même résultat qu'un appel d'une fonction non- constexpr équivalente dans le même contexte à tous égards, avec les exceptions suivantes :

constexpr constructeur

En plus des exigences des fonctions constexpr , un constructeur doit également satisfaire à toutes les conditions suivantes pour être adapté à constexpr :

  • Son corps de fonction est = delete ou satisfait aux exigences supplémentaires suivantes :
  • Si la classe est une union ayant des membres variants, exactement l'un d'entre eux est initialisé.
  • Si la classe est une classe union-like , mais n'est pas une union, pour chacun de ses membres union anonymes ayant des membres variants, exactement l'un d'entre eux est initialisé.
  • Chaque membre de données non-static non-variant et chaque sous-objet de classe de base est initialisé.
(jusqu'à C++20)
  • Si le constructeur est un constructeur déléguant , le constructeur cible est un constructeur constexpr .
  • Si le constructeur est un constructeur non-déléguant, chaque constructeur sélectionné pour initialiser les membres de données non-static et les sous-objets de classe de base est un constructeur constexpr .
(jusqu'à C++23)

Pour un constructeur constexpr qui n'est ni par défaut ni template, s'il n'existe aucune valeur d'argument telle qu'un appel de la fonction pourrait être une sous-expression évaluée de l'expression complète d'initialisation d'un objet soumis à une expression constante , le programme est mal formé, aucun diagnostic requis.

(jusqu'à C++23)

constexpr destructeur

Les destructeurs ne peuvent pas être constexpr , mais un destructeur trivial peut être appelé implicitement dans les expressions constantes.

(jusqu'à C++20)

En plus des exigences des fonctions constexpr , un destructeur doit également satisfaire toutes les conditions suivantes pour être éligible comme constexpr :

  • Pour chaque sous-objet de type classe ou tableau (éventuellement multidimensionnel) de celui-ci, ce type de classe doit avoir un destructeur constexpr .
(jusqu'à C++23)
  • La classe ne doit avoir aucune classe de base virtuelle.
(depuis C++20)

Notes

Étant donné que l'opérateur noexcept retourne toujours true pour une expression constante, il peut être utilisé pour vérifier si un appel particulier d'une fonction constexpr emprunte la branche d'expression constante :

constexpr int f(); 
constexpr bool b1 = noexcept(f()); // false, undefined constexpr function
constexpr int f() { return 0; }
constexpr bool b2 = noexcept(f()); // true, f() is a constant expression
(jusqu'en C++17)

Il est possible d'écrire une fonction constexpr dont l'invocation ne peut jamais satisfaire les exigences d'une expression constante de base :

void f(int& i) // not a constexpr function
{
    i = 0;
}
constexpr void g(int& i) // well-formed since C++23
{
    f(i); // unconditionally calls f, cannot be a constant expression
}
(depuis C++23)

Les constructeurs constexpr sont autorisés pour les classes qui ne sont pas des types littéraux. Par exemple, le constructeur par défaut de std::shared_ptr est constexpr, permettant une initialisation constante .

Les variables de référence peuvent être déclarées constexpr (leurs initialiseurs doivent être des expressions constantes de référence ) :

static constexpr int const& x = 42; // référence constexpr vers un objet const int
                                    // (l'objet a une durée de stockage statique
                                    //  en raison de l'extension de durée de vie par une référence statique)

Bien que try blocks et l'assemblage en ligne soient autorisés dans les fonctions constexpr, lever des exceptions qui ne sont pas capturées (depuis C++26) ou exécuter l'assemblage reste interdit dans une expression constante.

Si une variable a une destruction constante, il n'est pas nécessaire de générer du code machine pour appeler son destructeur, même si son destructeur n'est pas trivial.

Une fonction constexpr non-lambda, non-spéciale-membre et non-templatée ne peut pas devenir implicitement une fonction immédiate. Les utilisateurs doivent explicitement la marquer consteval pour rendre une telle définition de fonction valide.

(depuis C++20)
Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_constexpr 200704L (C++11) constexpr
201304L (C++14) Assouplissement de constexpr , méthodes constexpr non- const
201603L (C++17) Lambda constexpr
201907L (C++20) Initialisation par défaut triviale et déclaration asm dans les fonctions constexpr
202002L (C++20) Modification du membre actif d'une union lors de l'évaluation constante
202110L (C++23) Variables non- littérales , étiquettes et goto dans les fonctions constexpr
202207L (C++23) Assouplissement de certaines restrictions constexpr
202211L (C++23) Autorisation des variables static constexpr dans les fonctions constexpr
202306L (C++26) Conversion constexpr de void * : vers l'effacement de type constexpr
__cpp_constexpr_in_decltype 201711L (C++11)
(DR)
Génération de définitions de fonctions et de variables lorsque nécessaires pour l'évaluation constante
__cpp_constexpr_dynamic_alloc 201907L (C++20) Opérations pour la durée de stockage dynamique dans les fonctions constexpr

Mots-clés

constexpr

Exemple

Définit les fonctions constexpr C++11/14 qui calculent les factorielles ; définit un type littéral qui étend les littéraux de chaîne :

#include <iostream>
#include <stdexcept>
// Les fonctions constexpr de C++11 utilisent la récursion plutôt que l'itération
constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
// Les fonctions constexpr de C++14 peuvent utiliser des variables locales et des boucles
#if __cplusplus >= 201402L
constexpr int factorial_cxx14(int n)
{
    int res = 1;
    while (n > 1)
        res *= n--;
    return res;
}
#endif // C++14
// Une classe littérale
class conststr
{
    const char* p;
    std::size_t sz;
public:
    template<std::size_t N>
    constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {}
    // Les fonctions constexpr signalent les erreurs en lançant des exceptions
    // En C++11, elles doivent le faire depuis l'opérateur conditionnel ?:
    constexpr char operator[](std::size_t n) const
    {
        return n < sz ? p[n] : throw std::out_of_range("");
    }
    constexpr std::size_t size() const { return sz; }
};
// Les fonctions constexpr de C++11 devaient tout mettre dans une seule instruction return
// (C++14 n'a pas cette exigence)
constexpr std::size_t countlower(conststr s, std::size_t n = 0,
                                             std::size_t c = 0)
{
    return n == s.size() ? c :
        'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1)
                                   : countlower(s, n + 1, c);
}
// Une fonction de sortie qui nécessite une constante à la compilation, pour les tests
template<int n>
struct constN
{
    constN() { std::cout << n << '\n'; }
};
int main()
{
    std::cout << "4! = ";
    constN<factorial(4)> out1; // calculé à la compilation
    volatile int k = 8; // empêche l'optimisation en utilisant volatile
    std::cout << k << "! = " << factorial(k) << '\n'; // calculé à l'exécution
    std::cout << "Le nombre de lettres minuscules dans \"Hello, world!\" est ";
    constN<countlower("Hello, world!")> out2; // implicitement converti en conststr
    constexpr int a[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
    constexpr int length_a = sizeof a / sizeof(int); // std::size(a) en C++17,
                                                      // std::ssize(a) en C++20
    std::cout << "Le tableau de longueur " << length_a << " a pour éléments : ";
    for (int i = 0; i < length_a; ++i)
        std::cout << a[i] << ' ';
    std::cout << '\n';
}

Sortie :

4! = 24
8! = 40320
Le nombre de lettres minuscules dans "Hello, world!" est 9
Un tableau de longueur 12 a pour éléments : 0 1 2 3 4 5 6 7 8 0 0 0

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 1358 C++11 les fonctions constexpr template devaient aussi
avoir au moins une valeur d'argument valide
pas nécessaire
CWG 1359 C++11 les constructeurs constexpr d'union
devaient initialiser tous les membres de données
initialise exactement un membre
de données pour les unions non vides
CWG 1366 C++11 les classes avec des constructeurs constexpr dont les corps
sont = default ou = delete pouvaient avoir des classes de base virtuelles
ces classes ne peuvent
pas avoir de classes de base virtuelles
CWG 1595 C++11 les constructeurs délégués constexpr exigeaient
que tous les constructeurs impliqués soient constexpr
exige seulement que le constructeur
cible soit constexpr
CWG 1712 C++14 un template de variable constexpr devait avoir
toutes ses déclarations contenant le spécificateur constexpr [1]
n'est plus exigé
CWG 1911 C++11 les constructeurs constexpr pour les types non littéraux n'étaient pas autorisés autorisés dans l'initialisation constante
CWG 2004 C++11 la copie/déplacement d'une union avec un membre mutable
était autorisée dans une expression constante
les variantes mutables disqualifient
la copie/déplacement implicite
CWG 2022 C++98 le fait que des fonctions constexpr et non- constexpr
équivalentes produisent un résultat égal pouvait dépendre
de l'élimination de copie
suppose que l'élimination de copie est toujours
effectuée dans les expressions constantes
CWG 2163 C++14 les étiquettes étaient autorisées dans les fonctions constexpr
même si les instructions goto sont interdites
les étiquettes sont également interdites
CWG 2268 C++11 la copie/déplacement d'une union avec un membre mutable était
interdite par la résolution de CWG issue 2004
autorisée si l'objet est créé
dans l'expression constante
CWG 2278 C++98 la résolution de CWG issue 2022 n'était pas implémentable suppose que l'élimination de copie n'est jamais
effectuée dans les expressions constantes
CWG 2531 C++11 une variable non inline devenait inline
si elle est redéclarée avec constexpr
la variable ne
devient pas inline
  1. C'est redondant car il ne peut y avoir plus d'une déclaration d'un modèle de variable avec le spécificateur constexpr .

Voir aussi

expression constante définit une expression qui peut être évaluée à la compilation
consteval specifier (C++20) spécifie qu'une fonction est une fonction immédiate , c'est-à-dire que chaque appel à la fonction doit être dans une évaluation constante
constinit specifier (C++20) affirme qu'une variable a une initialisation statique, c'est-à-dire initialisation à zéro et initialisation constante
Documentation C pour constexpr