Namespaces
Variants

Function declaration

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

Une déclaration de fonction introduit le nom de la fonction et son type. Une définition de fonction associe le nom/type de la fonction au corps de la fonction.

Table des matières

Déclaration de fonction

Les déclarations de fonctions peuvent apparaître dans n'importe quelle portée. Une déclaration de fonction au niveau de la portée de classe introduit une fonction membre de classe (sauf si le friend est utilisé), voir fonctions membres et fonctions amies pour plus de détails.

noptr-declarator ( parameter-list ) cv  (optionnel) ref   (optionnel) except  (optionnel) attr  (optionnel) (1)
noptr-declarator ( parameter-list ) cv  (optionnel) ref   (optionnel) except  (optionnel) attr  (optionnel)
-> trailing
(2) (depuis C++11)

(voir Declarations pour les autres formes de la syntaxe du declarator )

1) Syntaxe de déclaration de fonction régulière.
2) Déclaration de type de retour en suffixe. La decl-specifier-seq dans ce cas doit contenir le mot-clé auto .
noptr-declarator - tout declarator valide, mais s'il commence par * , & , ou && , il doit être entouré de parenthèses.
parameter-list - liste éventuellement vide, séparée par des virgules des paramètres de la fonction (voir ci-dessous pour les détails)
attr - (depuis C++11) une liste d' attributs . Ces attributs sont appliqués au type de la fonction, et non à la fonction elle-même. Les attributs pour la fonction apparaissent après l'identifiant dans le déclarateur et sont combinés avec les attributs qui apparaissent au début de la déclaration, le cas échéant.
cv - qualification const/volatile, uniquement autorisée dans les déclarations de fonctions membres non statiques
ref - (depuis C++11) ref-qualification, uniquement autorisée dans les déclarations de fonctions membres non statiques
except -

spécification d'exception dynamique

(jusqu'à C++11)

soit spécification d'exception dynamique
ou spécification noexcept

(depuis C++11)
(jusqu'à C++17)

spécification noexcept

(depuis C++17)
trailing - Type de retour trailing, utile si le type de retour dépend des noms d'arguments, comme dans template < class T, class U > auto add ( T t, U u ) - > decltype ( t + u ) ; ou est complexe, comme dans auto fpif ( int ) - > int ( * ) ( int )


Comme mentionné dans Declarations , le déclarateur peut être suivi d'une requires clause , qui déclare les constraints associées pour la fonction, qui doivent être satisfaites afin que la fonction soit sélectionnée par la overload resolution . (exemple : void f1 ( int a ) requires true ; ) Notez que la contrainte associée fait partie de la signature de la fonction, mais ne fait pas partie du type de fonction.

(depuis C++20)

Les déclarateurs de fonction peuvent être mélangés avec d'autres déclarateurs, lorsque la séquence de spécificateurs de déclaration le permet :

// déclare un int, un int*, une fonction, et un pointeur vers une fonction
int a = 1, *p = NULL, f(), (*pf)(double);
// decl-specifier-seq est int
// le déclarateur f() déclare (mais ne définit pas)
//                une fonction ne prenant aucun argument et retournant int
struct S
{
    virtual int f(char) const, g(int) &&; // déclare deux fonctions membres non statiques
    virtual int f(char), x; // erreur de compilation : virtual (dans decl-specifier-seq)
                            // est uniquement autorisé dans les déclarations de
                            // fonctions membres non statiques
};

L'utilisation d'un type d'objet qualifié volatile comme type de paramètre ou type de retour est dépréciée.

(since C++20)

Le type de retour d'une fonction ne peut pas être un type fonction ou un type tableau (mais peut être un pointeur ou une référence vers ceux-ci).

Comme pour toute déclaration, les attributs qui apparaissent avant la déclaration et ceux qui apparaissent immédiatement après l'identifiant dans le déclarateur s'appliquent tous deux à l'entité déclarée ou définie (dans ce cas, à la fonction) :

[[noreturn]] void f [[noreturn]] (); // OK: both attributes apply to the function f

Cependant, les attributs qui apparaissent après le déclarateur (dans la syntaxe ci-dessus) s'appliquent au type de la fonction, et non à la fonction elle-même :

void f() [[noreturn]]; // Error: this attribute has no effect on the function itself
(depuis C++11)

Déduction du type de retour

Si la decl-specifier-seq de la déclaration de fonction contient le mot-clé auto , le type de retour trailing peut être omis, et sera déduit par le compilateur à partir du type de l'opérande utilisé dans l'instruction non-discarded return . Si le type de retour n'utilise pas decltype ( auto ) , la déduction suit les règles de template argument deduction :

int x = 1;
auto f() { return x; }        // type de retour est int
const auto& f() { return x; } // type de retour est const int&

Si le type de retour est decltype ( auto ) , le type de retour correspond à ce qui serait obtenu si l'opérande utilisé dans l'instruction return était encapsulé dans decltype :

int x = 1;
decltype(auto) f() { return x; }  // type de retour est int, identique à decltype(x)
decltype(auto) f() { return(x); } // type de retour est int&, identique à decltype((x))

(note: “ const decltype ( auto ) & ” est une erreur, decltype ( auto ) doit être utilisé seul)

S'il y a plusieurs instructions de retour, elles doivent toutes déduire le même type :

auto f(bool val)
{
    if (val) return 123; // déduit le type de retour int
    else return 3.14f;   // Erreur : déduit le type de retour float
}

S'il n'y a pas d'instruction return ou si l'opérande de l'instruction return est une expression void (y compris les instructions return sans opérande), le type de retour déclaré doit être soit decltype ( auto ) , auquel cas le type de retour déduit est void , ou (éventuellement qualifié cv) auto , auquel cas le type de retour déduit est alors (identique qualifié cv) void :

auto f() {}              // retourne void
auto g() { return f(); } // retourne void
auto* x() {}             // Erreur : impossible de déduire auto* à partir de void

Une fois qu'une instruction return a été rencontrée dans une fonction, le type de retour déduit à partir de cette instruction peut être utilisé dans le reste de la fonction, y compris dans d'autres instructions return :

auto sum(int i)
{
    if (i == 1)
        return i;              // le type de retour de sum est int
    else
        return sum(i - 1) + i; // OK : le type de retour de sum est déjà connu
}

Si l'instruction return utilise une liste d'initialisation entre accolades , la déduction n'est pas autorisée :

auto func() { return {1, 2, 3}; } // Erreur

Fonctions virtuelles et coroutines (depuis C++20) ne peuvent pas utiliser la déduction du type de retour :

struct F
{
    virtual auto f() { return 2; } // Erreur
};

Modèles de fonction autres que les fonctions de conversion définies par l'utilisateur peuvent utiliser la déduction du type de retour. La déduction a lieu lors de l'instanciation même si l'expression dans l'instruction return n'est pas dépendante . Cette instanciation n'est pas dans un contexte immédiat pour les besoins de SFINAE .

template<class T>
auto f(T t) { return t; }
typedef decltype(f(1)) fint_t;    // instancie f<int> pour déduire le type de retour
template<class T>
auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; } // instancie les deux f pour déterminer les types de retour,
                                  // choisit la seconde surcharge de template

Les redéclarations ou spécialisations de fonctions ou de modèles de fonction qui utilisent la déduction du type de retour doivent utiliser les mêmes espaces réservés pour le type de retour :

auto f(int num) { return num; }
// int f(int num);            // Erreur : pas de type de retour substitut
// decltype(auto) f(int num); // Erreur : substitut différent
template<typename T>
auto g(T t) { return t; }
template auto g(int);     // OK : le type de retour est int
// template char g(char); // Erreur : pas une spécialisation du template principal g

De même, les redéclarations ou spécialisations de fonctions ou de modèles de fonction qui n'utilisent pas la déduction de type de retour ne doivent pas utiliser un espace réservé :

int f(int num);
// auto f(int num) { return num; } // Erreur : pas une redéclaration de f
template<typename T>
T g(T t) { return t; }
template int g(int);      // OK : spécialisation de T en int
// template auto g(char); // Erreur : pas une spécialisation du template primaire g

Les déclarations d'instanciation explicite n'instancient pas elles-mêmes les modèles de fonction qui utilisent la déduction de type de retour :

template<typename T>
auto f(T t) { return t; }
extern template auto f(int); // n'instancie pas f<int>
int (*p)(int) = f; // instancie f<int> pour déterminer son type de retour,
                   // mais une définition d'instanciation explicite
                   // est toujours requise quelque part dans le programme
(depuis C++14)

Liste des paramètres

La liste de paramètres détermine les arguments qui peuvent être spécifiés lors de l'appel de la fonction. Il s'agit d'une liste séparée par des virgules de déclarations de paramètres , dont chacune a la syntaxe suivante :

attr  (optionnel) decl-specifier-seq declarator (1)

attr  (optionnel) this decl-specifier-seq declarator

(2) (depuis C++23)
attr  (optionnel) decl-specifier-seq declarator = initializer (3)
attr  (optionnel) decl-specifier-seq abstract-declarator  (optionnel) (4)

attr  (optionnel) this decl-specifier-seq abstract-declarator  (optionnel)

(5) (depuis C++23)
attr  (optionnel) decl-specifier-seq abstract-declarator  (optionnel) = initializer (6)
void (7)
1) Déclare un paramètre nommé (formel). Pour les significations de decl-specifier-seq et declarator , voir declarations .
int f ( int a, int * p, int ( * ( * x ) ( double ) ) [ 3 ] ) ;
**Note:** Le code C++ n'a pas été traduit conformément aux instructions, car il se trouve dans des balises spécifiques au code et contient des termes techniques C++ qui doivent rester en anglais. Seul le texte environnant (s'il y en avait) aurait été traduit en français.
2) Déclare un paramètre objet explicite nommé.
3) Déclare un paramètre nommé (formel) avec une valeur par défaut .
int f ( int a = 7 , int * p = nullptr, int ( * ( * x ) ( double ) ) [ 3 ] = nullptr ) ;
**Note:** Le code C++ n'a pas été traduit car il se trouve dans des balises ` ` qui sont considérées comme équivalentes à des balises ` ` pour la préservation du code source. La structure HTML et tous les termes techniques C++ (`int`, `double`, `nullptr`, etc.) ont été conservés intacts.
4) Déclare un paramètre sans nom.
int f ( int , int * , int ( * ( * ) ( double ) ) [ 3 ] ) ;
**Note:** Le code C++ n'a pas été traduit conformément aux instructions, car il se trouve dans des balises HTML et contient des termes spécifiques au C++. Seul le texte en dehors des balises de code aurait été traduit, mais dans cet exemple, il n'y a pas de texte à traduire en dehors du code C++.
5) Déclare un explicit object parameter sans nom.
6) Déclare un paramètre sans nom avec une valeur par défaut .
int f ( int = 7 , int * = nullptr, int ( * ( * ) ( double ) ) [ 3 ] = nullptr ) ;
**Note:** Le code C++ n'a pas été traduit conformément aux instructions, car il se trouve dans des balises spécifiques au code et contient des termes techniques C++ qui doivent rester en anglais pour préserver leur signification technique exacte.
7) Indique que la fonction ne prend aucun paramètre, c'est l'exact synonyme d'une liste de paramètres vide : int f ( void ) ; et int f ( ) ; déclarent la même fonction.
void est la seule syntaxe équivalente à une liste de paramètres vide, les autres utilisations des paramètres void sont mal formées :
Utilisation incorrecte Exemple
plusieurs paramètres sont présents int f1 ( void , int ) ;
le paramètre void est nommé int f2 ( void param ) ;
void est qualifié CV int f3 ( const void ) ;
void est dépendant int f4 ( T ) ; (où T est void )
le paramètre void est un paramètre objet explicite (depuis C++23) int f5 ( this void ) ;

Bien que decl-specifier-seq implique qu'il puisse exister des spécificateurs autres que les spécificateurs de type, le seul autre spécificateur autorisé est register ainsi que auto (jusqu'à C++11) , et il n'a aucun effet.

(jusqu'à C++17)

Si l'un des paramètres de fonction utilise un espace réservé (soit auto ou un type concept ), la déclaration de fonction est plutôt une déclaration de modèle de fonction abrégé :

void f1(auto);    // same as template<class T> void f1(T)
void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
(depuis C++20)

Une déclaration de paramètre avec le spécificateur this (syntaxe ( 2 ) / ( 5 ) ) déclare un paramètre objet explicite .

Un paramètre objet explicite ne peut pas être un pack de paramètres de fonction , et il ne peut apparaître que comme premier paramètre de la liste de paramètres dans les déclarations suivantes :

Une fonction membre avec un paramètre objet explicite a les restrictions suivantes :

  • La fonction n'est pas static .
  • La fonction n'est pas virtual .
  • Le déclarateur de la fonction ne contient pas cv et ref .
struct C
{
    void f(this C& self);     // OK
    template<typename Self>
    void g(this Self&& self); // also OK for templates
    void p(this C) const;     // Error: “const” not allowed here
    static void q(this C);    // Error: “static” not allowed here
    void r(int, this C);      // Error: an explicit object parameter
                              //        can only be the first parameter
};
// void func(this C& self);   // Error: non-member functions cannot have
                              //        an explicit object parameter
(depuis C++23)

Les noms de paramètres déclarés dans les déclarations de fonctions sont généralement uniquement à des fins d'auto-documentation. Ils sont utilisés (mais restent optionnels) dans les définitions de fonctions.

Une ambiguïté survient dans une liste de paramètres lorsqu'un nom de type est imbriqué entre parenthèses (incluant les expressions lambda ) (depuis C++11) . Dans ce cas, le choix se situe entre la déclaration d'un paramètre de type pointeur vers fonction et la déclaration d'un paramètre avec des parenthèses redondantes autour de l'identifiant du déclarateur . La résolution consiste à considérer le nom de type comme un spécificateur de type simple (qui est le type pointeur vers fonction) :

class C {};
void f(int(C)) {} // void f(int(*fp)(C param)) {}
                  // PAS void f(int C) {}
void g(int *(C[10])); // void g(int *(*fp)(C param[10]));
                      // PAS void g(int *C[10]);
**Traductions effectuées :** - "Do not translate" → Conservé tel quel (instruction de travail) - "NOT" → "PAS" (dans les commentaires) - Les commentaires en anglais ont été traduits en français - Toutes les balises HTML, attributs et code C++ sont préservés intacts

Le type de paramètre ne peut pas être un type qui inclut une référence ou un pointeur vers un tableau de taille inconnue, y compris les pointeurs/tableaux multi-niveaux de tels types, ou un pointeur vers des fonctions dont les paramètres sont de tels types.

Utilisation des points de suspension

Le dernier paramètre dans la liste des paramètres peut être une ellipse ( ... ) ; cela déclare une fonction variadique . La virgule précédant l'ellipse peut être omise (obsolète en C++26) :

int printf(const char* fmt, ...); // une fonction variadique
int printf(const char* fmt...);   // identique à ci-dessus, mais obsolète depuis C++26
template<typename... Args>
void f(Args..., ...); // un modèle de fonction variadique avec un pack de paramètres
template<typename... Args>
void f(Args... ...);  // identique à ci-dessus, mais obsolète depuis C++26
template<typename... Args>
void f(Args......);   // identique à ci-dessus, mais obsolète depuis C++26

Type de fonction

Liste des types de paramètres

La parameter-type-list d'une fonction est déterminée comme suit :

  1. Le type de chaque paramètre (incluant les parameter packs de fonction) (depuis C++11) est déterminé à partir de sa propre déclaration de paramètre .
  2. Après avoir déterminé le type de chaque paramètre, tout paramètre de type « tableau de T » ou de type fonction T est ajusté en « pointeur vers T ».
  3. Après avoir produit la liste des types de paramètres, toutes les cv-qualifiers de plus haut niveau modifiant un type de paramètre sont supprimées lors de la formation du type de fonction.
  4. La liste résultante des types de paramètres transformés et la présence ou l'absence de l' ellipsis ou d'un parameter pack de fonction (depuis C++11) constitue la parameter-type-list de la fonction.
void f(char*);         // #1
void f(char[]) {}      // définit #1
void f(const char*) {} // OK, une autre surcharge
void f(char* const) {} // Erreur : redéfinit #1
void g(char(*)[2]);   // #2
void g(char[3][2]) {} // définit #2
void g(char[3][3]) {} // OK, une autre surcharge
void h(int x(const int)); // #3
void h(int (*)(int)) {}   // définit #3

Détermination du type de fonction

Dans la syntaxe (1) , en supposant que noptr-declarator soit une déclaration autonome, étant donné le type du qualified-id ou unqualified-id dans noptr-declarator comme « derived-declarator-type-list T » :

  • Si la spécification d'exception est non-lancante , le type de la fonction déclarée est
    « liste-de-déclarateurs-dérivés noexcept fonction de
    liste-de-types-de-paramètres cv  (optionnel) ref   (optionnel) retournant T ».
(depuis C++17)
  • Le (jusqu'en C++17) Sinon, le (depuis C++17) type de la fonction déclarée est
    « liste-de-type-déclarateur-dérivé fonction de
    liste-de-type-de-paramètres cv  (optionnel) ref   (optionnel) (depuis C++11) retournant T ».

Dans la syntaxe (2) , en supposant noptr-declarator comme une déclaration autonome, étant donné le type du qualified-id ou unqualified-id dans noptr-declarator comme « derived-declarator-type-list T » ( T doit être auto dans ce cas) :

(depuis C++11)
  • Si la spécification d'exception est non-lancante , le type de la fonction déclarée est
    « derived-declarator-type-list noexcept fonction de
    parameter-type-list cv  (optionnel) ref   (optionnel) retournant trailing  ».
(depuis C++17)
  • Le (jusqu'à C++17) Sinon, le (depuis C++17) type de la fonction déclarée est
    « derived-declarator-type-list fonction de
    parameter-type-list cv  (optionnel) ref   (optionnel) retournant trailing  ».

attr , s'il est présent, s'applique au type de fonction.

(depuis C++11)
// le type de « f1 » est
// « fonction de int retournant void, avec l'attribut noreturn »
void f1(int a) [[noreturn]];
// le type de « f2 » est
// « fonction constexpr noexcept de pointeur vers int retournant int »
constexpr auto f2(int[] b) noexcept -> int;
struct X
{
    // le type de « f3 » est
    // « fonction de aucun paramètre const retournant const int »
    const int f3() const;
};

Qualificateurs en fin de déclaration

Un type de fonction avec cv  ou ref   (depuis C++11) (incluant un type nommé par typedef ) ne peut apparaître que comme :

typedef int FIC(int) const;
FIC f;     // Erreur : ne déclare pas une fonction membre
struct S
{
    FIC f; // OK
};
FIC S::*pm = &S::f; // OK

Signature de fonction

Chaque fonction possède une signature.

La signature d'une fonction consiste en son nom et sa liste-de-types-de-paramètres . Sa signature inclut également l'espace de noms englobant namespace , avec les exceptions suivantes :

  • Si la fonction est une fonction membre , sa signature contient la classe dont la fonction est membre au lieu de l'espace de noms englobant. Sa signature contient également les composants suivants, s'ils existent :
  • cv
**Note:** Le texte "cv" n'a pas été traduit car il s'agit d'un terme spécifique au C++ (cv-qualifiers) et doit être conservé dans sa forme originale conformément aux instructions.
  • ref
(depuis C++11)
  • clause requires finale
  • Si la fonction est une fonction friend non-template avec une clause requires finale, sa signature contient la classe englobante au lieu de l'espace de noms englobant. La signature contient également la clause requires finale.
(depuis C++20)

except et attr (depuis C++11) n'affecte pas la signature de fonction , bien que la noexcept specification affecte le type de fonction (depuis C++17) .

Définition de fonction

Une définition de fonction non membre peut apparaître uniquement au niveau de la portée du namespace (il n'existe pas de fonctions imbriquées). Une fonction membre peut également être définie dans le corps d'une définition de classe . Elles ont la syntaxe suivante :

attr  (optionnel) decl-specifier-seq  (optionnel) declarator
virt-specs  (optionnel) contract-specs  (optionnel) function-body
(1)
attr  (optionnel) decl-specifier-seq  (optionnel) declarator
requires-clause contract-specs  (optionnel) function-body
(2) (depuis C++20)
1) Une définition de fonction sans contraintes.
2) Une définition de fonction avec des contraintes.
attr - (depuis C++11) une liste d' attributs . Ces attributs sont combinés avec les attributs après l'identifiant dans le déclarateur (voir en haut de cette page), s'il y en a.
decl-specifier-seq - le type de retour avec spécificateurs, comme dans la grammaire de déclaration
declarator - déclarateur de fonction, identique à la grammaire de déclaration de fonction ci-dessus (peut être parenthésé)
virt-specs - (depuis C++11) override , final , ou leur combinaison dans n'importe quel ordre
requires-clause - une clause requires
contract-specs - (depuis C++26) une liste de spécificateurs de contrat de fonction
function-body - le corps de fonction (voir ci-dessous)


function-body est l'un des éléments suivants :

ctor-initializer  (optionnel) compound-statement (1)
function-try-block (2)
= default ; (3) (depuis C++11)
= delete ; (4) (depuis C++11)
= delete ( string-literal ); (5) (depuis C++26)
1) Corps de fonction régulier.
3) Définition de fonction explicitement définie par défaut.
4) Définition de fonction explicitement supprimée.
5) Définition de fonction explicitement supprimée avec message d'erreur.
ctor-initializer - liste d'initialisation des membres , uniquement autorisée dans les constructeurs
compound-statement - la séquence d'instructions entre accolades qui constitue le corps d'une fonction
function-try-block - un bloc try de fonction
string-literal - un littéral de chaîne non évalué qui pourrait être utilisé pour expliquer la raison pour laquelle la fonction est supprimée
int max(int a, int b, int c)
{
    int m = (a > b) ? a : b;
    return (m > c) ? m : c;
}
// la séquence de spécificateurs de déclaration est « int »
// le déclarateur est « max(int a, int b, int c) »
// le corps est { ... }

Le corps de la fonction est une instruction composée (séquence de zéro ou plusieurs instructions entourées d'une paire d'accolades), qui est exécutée lors de l'appel de la fonction. De plus, le corps de la fonction d'un constructeur inclut également les éléments suivants :

Si une définition de fonction contient un virt-specs , elle doit définir une fonction membre .

(depuis C++11)

Si une définition de fonction contient une requires-clause , elle doit définir une fonction template .

(depuis C++20)
void f() override {} // Erreur : pas une fonction membre
void g() requires (sizeof(int) == 4) {} // Erreur : pas une fonction template

Les types des paramètres, ainsi que le type de retour d'une définition de fonction ne peuvent pas être (éventuellement qualifiés cv) incomplete class types sauf si la fonction est définie comme supprimée (depuis C++11) . La vérification de complétude n'est effectuée que dans le corps de la fonction, ce qui permet aux member functions de retourner la classe dans laquelle elles sont définies (ou sa classe englobante), même si elle est incomplète au point de définition (elle est complète dans le corps de la fonction).

Les paramètres déclarés dans le déclarateur d'une définition de fonction sont dans la portée à l'intérieur du corps. Si un paramètre n'est pas utilisé dans le corps de la fonction, il n'est pas nécessaire de le nommer (il suffit d'utiliser un déclarateur abstrait) :

void print(int a, int) // deuxième paramètre non utilisé
{
    std::printf("a = %d\n", a);
}

Bien que les cv-qualifiers de plus haut niveau sur les paramètres soient ignorés dans les déclarations de fonction, ils modifient le type du paramètre tel qu'il est visible dans le corps d'une fonction :

void f(const int n) // déclare une fonction de type void(int)
{
    // mais dans le corps, le type de « n » est const int
}

Fonctions par défaut

Si la définition de la fonction est de syntaxe ( 3 ) , la fonction est définie comme explicitement définie par défaut .

Une fonction explicitement définie par défaut doit être une fonction membre spéciale ou une fonction d'opérateur de comparaison (depuis C++20) , et elle ne doit pas avoir d' argument par défaut .

Une fonction membre spéciale explicitement définie par défaut F1 est autorisée à différer de la fonction membre spéciale correspondante F2 qui aurait été implicitement déclarée, comme suit :

  • F1 et F2 peuvent avoir des spécifications ref et/ou except différentes.
  • Si F2 possède un paramètre non-objet de type const C & , le paramètre non-objet correspondant de F1 peut être de type C& .
  • Si F2 possède un paramètre objet implicite de type « référence vers C », F1 peut être une fonction membre à objet explicite dont le paramètre objet explicite est de type (éventuellement différent) « référence vers C », auquel cas le type de F1 différerait du type de F2 en ce que le type de F1 possède un paramètre supplémentaire.
(depuis C++23)

Si le type de F1 diffère du type de F2 d'une manière autre que celle autorisée par les règles précédentes, alors :

  • Si F1 est un opérateur d'affectation, et que le type de retour de F1 diffère du type de retour de F2 ou que le type de paramètre non-objet de F1 n'est pas une référence, le programme est mal formé.
  • Sinon, si F1 est explicitement déclaré par défaut dans sa première déclaration, il est défini comme supprimé.
  • Sinon, le programme est mal formé.

Une fonction explicitement définie par défaut lors de sa première déclaration est implicitement inline , et est implicitement constexpr si elle peut être une fonction constexpr .

struct S
{
    S(int a = 0) = default;             // erreur : argument par défaut
    void operator=(const S&) = default; // erreur : type de retour non correspondant
    ~S() noexcept(false) = default;     // OK, spécification d'exception différente
private:
    int i;
    S(S&);          // OK, constructeur de copie privé
};
S::S(S&) = default; // OK, définit le constructeur de copie

Les fonctions explicitement définies par défaut et les fonctions implicitement déclarées sont collectivement appelées fonctions par défaut . Leurs définitions réelles seront fournies implicitement, consultez leurs pages correspondantes pour plus de détails.

Fonctions supprimées

Si la définition de la fonction est de syntaxe ( 4 ) ou ( 5 ) (depuis C++26) , la fonction est définie comme explicitement supprimée .

Toute utilisation d'une fonction supprimée est mal formée (le programme ne compilera pas). Cela inclut les appels, explicites (avec un opérateur d'appel de fonction) et implicites (un appel à un opérateur surchargé supprimé, une fonction membre spéciale, une fonction d'allocation, etc.), la construction d'un pointeur ou d'un pointeur-vers-membre vers une fonction supprimée, et même l'utilisation d'une fonction supprimée dans une expression qui n'est pas potentiellement évaluée .

Une fonction membre virtuelle non pure peut être définie comme supprimée, même si elle est implicitement odr-used . Une fonction supprimée ne peut être remplacée que par des fonctions supprimées, et une fonction non supprimée ne peut être remplacée que par des fonctions non supprimées.

Si string-literal est présent, l'implémentation est encouragée à inclure son texte comme partie du message de diagnostic résultant qui montre la justification de la suppression ou pour suggérer une alternative.

(depuis C++26)

Si la fonction est surchargée, la résolution de surcharge a lieu en premier, et le programme est seulement mal-formé si la fonction supprimée a été sélectionnée :

struct T
{
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete("new[] is deleted"); // depuis C++26
};
T* p = new T;    // Erreur : tente d'appeler T::operator new supprimé
T* p = new T[5]; // Erreur : tente d'appeler T::operator new[] supprimé,
                 //        émet un message de diagnostic "new[] is deleted"

La définition supprimée d'une fonction doit être la première déclaration dans une unité de traduction : une fonction précédemment déclarée ne peut pas être redéclarée comme supprimée :

struct T { T(); };
T::T() = delete; // Erreur : doit être supprimé lors de la première déclaration

Fonctions fournies par l'utilisateur

Une fonction est fournie par l'utilisateur si elle est déclarée par l'utilisateur et non explicitement définie par défaut ou supprimée lors de sa première déclaration. Une fonction explicitement définie par défaut fournie par l'utilisateur (c'est-à-dire explicitement définie par défaut après sa première déclaration) est définie au point où elle est explicitement définie par défaut ; si une telle fonction est implicitement définie comme supprimée, le programme est non valide. Déclarer une fonction comme définie par défaut après sa première déclaration peut offrir une exécution efficace et une définition concise tout en permettant une interface binaire stable pour une base de code en évolution.

// Toutes les fonctions membres spéciales de « trivial » sont
// définies par défaut sur leurs premières déclarations respectivement,
// elles ne sont pas fournies par l'utilisateur
struct trivial
{
    trivial() = default;
    trivial(const trivial&) = default;
    trivial(trivial&&) = default;
    trivial& operator=(const trivial&) = default;
    trivial& operator=(trivial&&) = default;
    ~trivial() = default;
};
struct nontrivial
{
    nontrivial(); // première déclaration
};
// non définie par défaut sur la première déclaration,
// elle est fournie par l'utilisateur et est définie ici
nontrivial::nontrivial() = default;

Résolution d'Ambiguïté

En cas d'ambiguïté entre un corps de fonction et un initialiseur commençant par { ou = (depuis C++26) , l'ambiguïté est résolue en vérifiant le type de l' identifiant du déclarateur du noptr-declarator :

  • Si le type est un type de fonction, la séquence de jetons ambigus est traitée comme un corps de fonction.
  • Sinon, la séquence de jetons ambigus est traitée comme un initialiseur.
using T = void(); // type de fonction
using U = int;    // type non-fonction
T a{}; // définit une fonction ne faisant rien
U b{}; // initialise par valeur un objet int
T c = delete("hello"); // définit une fonction comme supprimée
U d = delete("hello"); // initialise par copie un objet int avec
                       // le résultat d'une expression delete (mal formé)

__func__

Dans le corps de la fonction, la variable prédéfinie locale à la fonction __func__ est définie comme si par

static const char __func__[] = "nom-de-fonction";

Cette variable a une portée de bloc et une durée de stockage statique :

struct S
{
    S(): s(__func__) {} // OK : la liste d'initialisation fait partie du corps de la fonction
    const char* s;
};
void f(const char* s = __func__); // Erreur : la liste de paramètres fait partie du déclarateur
#include <iostream>
void Foo() { std::cout << __func__ << ' '; }
struct Bar
{
    Bar() { std::cout << __func__ << ' '; }
    ~Bar() { std::cout << __func__ << ' '; }
    struct Pub { Pub() { std::cout << __func__ << ' '; } };
};
int main()
{
    Foo();
    Bar bar;
    Bar::Pub pub;
}

Sortie possible :

Foo Bar Pub ~Bar
(depuis C++11)

Spécificateurs de contrat de fonction

Les déclarations de fonctions et les expressions lambda peuvent contenir une séquence de spécificateurs de contrat de fonction  , chaque spécificateur a la syntaxe suivante :

pre attr  (optionnel) ( prédicat ) (1)
post attr  (optionnel) ( prédicat ) (2)
post attr  (optionnel) ( identifiant result-attr  (optionnel) : prédicat ) (3)
1) Introduit une assertion de précondition  .
2,3) Introduit une assertion de postcondition  .
2) L'assertion ne se lie pas au résultat.
3) L'assertion se lie au résultat.
attr - une liste d'attributs se rapportant à l'assertion de contrat introduite
predicate - toute expression (sauf les expressions virgules non parenthésées)
identifier - l'identifiant qui fait référence au résultat
result-attr - une liste d'attributs se rapportant à la liaison du résultat


L'assertion de précondition et l'assertion de postcondition sont collectivement appelées assertion de contrat de fonction  .

Une assertion de contrat de fonction est une assertion de contrat associée à une fonction. Le prédicat d'une assertion de contrat de fonction est son prédicat converti contextuellement en bool .

Les fonctions suivantes ne peuvent pas être déclarées avec des spécificateurs de contrat de fonction :

Assertions de précondition

Une assertion de précondition est associée à l'entrée d'une fonction :

int divide(int dividend, int divisor) pre(divisor != 0)
{
    return dividend / divisor;
}
double square_root(double num) pre(num >= 0)
{
    return std::sqrt(num);
}
**Note:** Le code C++ n'a pas été traduit car il se trouve dans des balises `
` et contient des termes spécifiques au C++ qui doivent être préservés selon vos instructions. Seul le texte environnant (s'il y en avait) aurait été traduit en français.

Assertions de postcondition

Une assertion de postcondition est associée à la sortie normale d'une fonction.

Si une assertion de postcondition possède un identifiant , le spécificateur de contrat de fonction introduit identifiant comme nom d'une liaison de résultat de la fonction associée. Une liaison de résultat désigne l'objet ou la référence retournée par l'invocation de cette fonction. Le type d'une liaison de résultat est le type de retour de sa fonction associée.

int absolute_value(int num) post(r : r >= 0)
{
    return std::abs(num);
}
double sine(double num) post(r : r >= -1.0 && r <= 1.0)
{
    if (std::isnan(num) || std::isinf(num))
        // la sortie via une exception ne cause jamais de violation de contrat
        throw std::invalid_argument("Argument invalide");
    return std::sin(num);
}

Si une assertion de postcondition possède un identifiant , et que le type de retour de la fonction associée est (éventuellement qualifié cv) void , le programme est mal formé :

void f() post(r : r > 0); // Erreur : aucune valeur ne peut être liée à « r »

Lorsque le type de retour déclaré d'une fonction non template contient un type substitut , une assertion de postcondition avec un identifiant  ne peut apparaître que dans une définition de fonction :

auto g(auto&) post(r : r >= 0); // OK, « g » est un template
auto h() post(r : r >= 0);      // Erreur : impossible de nommer la valeur de retour
auto k() post(r : r >= 0)       // OK, « k » est une définition
{
    return 0;
}

Cohérence des contrats

Une redéclaration D d'une fonction ou d'un modèle de fonction func doit avoir soit aucune spécification de contrat soit les mêmes spécifications de contrat que toute première déclaration F accessible depuis D . Si D et F sont dans différentes unités de traduction, un diagnostic n'est requis que si D est attachée à un module nommé.

Si une déclaration F1 est une première déclaration de func dans une unité de traduction et qu'une déclaration F2 est une première déclaration de func dans une autre unité de traduction, F1 et F2 doivent spécifier les mêmes contract-specs , aucun diagnostic requis.

Deux contract-specs sont identiques s'ils consistent en les mêmes spécificateurs de contrat de fonction dans le même ordre.

Un spécificateur de contrat de fonction C1 sur une déclaration de fonction D1 est identique à un spécificateur de contrat de fonction C2 sur une déclaration de fonction D2 si toutes les conditions suivantes sont satisfaites :

  • Les prédicats de C1 et C2 satisferaient la règle de définition unique s'ils étaient placés dans des définitions de fonction sur les déclarations D1 et D2 (si D1 et D2 sont dans différentes unités de traduction, les entités correspondantes définies dans chaque prédicat se comportent comme s'il y avait une seule entité avec une seule définition), respectivement, à l'exception des renommages suivants :
    • Le renommage des paramètres de la fonction déclarée.
    • Le renommage des paramètres de template d'un template englobant la fonction déclarée.
    • Le renommage de la liaison de résultat (le cas échéant).
  • Les deux C1 et C2 ont un identifiant ou aucun n'en a.

Si cette condition n'est pas satisfaite uniquement en raison de la comparaison de deux expressions lambda qui sont contenues dans les prédicats , aucun diagnostic n'est requis.

bool b1, b2;
void f() pre (b1) pre([]{ return b2; }());
void f(); // OK, spécificateurs de contrat de fonction omis
void f() pre (b1) pre([]{ return b2; }()); // Erreur : les fermetures ont des types différents
void f() pre (b1); // Erreur : les spécificateurs de contrat de fonction sont différents
int g() post(r : b1);
int g() post(b1); // Erreur : aucune liaison de résultat
namespace N
{
    void h() pre (b1);
    bool b1;
    void h() pre (b1); // Erreur : les spécificateurs de contrat de fonction diffèrent
                       //        selon la règle de définition unique
}
(depuis C++26)

Notes

En cas d'ambiguïté entre une déclaration de variable utilisant la syntaxe de l'initialisation directe et une déclaration de fonction, le compilateur choisit toujours la déclaration de fonction ; voir direct-initialization .

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_decltype_auto 201304L (C++14) decltype(auto)
__cpp_return_type_deduction 201304L (C++14) déduction du type de retour pour les fonctions normales
__cpp_explicit_this_parameter 202110L (C++23) paramètres objet explicites ( déduction de this )
__cpp_deleted_function 202403L (C++26) fonction supprimée avec une raison

Mots-clés

default , delete , pre , post

Exemple

#include <iostream>
#include <string>
// fonction simple avec un argument par défaut, ne retournant rien
void f0(const std::string& arg = "world!")
{
    std::cout << "Hello, " << arg << '\n';
}
// la déclaration est dans la portée du namespace (fichier)
// (la définition est fournie plus tard)
int f1();
// fonction retournant un pointeur vers f0, style pré-C++11
void (*fp03())(const std::string&)
{
    return f0;
}
// fonction retournant un pointeur vers f0, avec type de retour trailing C++11
auto fp11() -> void(*)(const std::string&)
{
    return f0;
}
int main()
{
    f0();
    fp03()("test!");
    fp11()("again!");
    int f2(std::string) noexcept; // déclaration dans la portée de la fonction
    std::cout << "f2(\"bad\"): " << f2("bad") << '\n';
    std::cout << "f2(\"42\"): " << f2("42") << '\n';
}
// fonction non-membre simple retournant int
int f1()
{
    return 007;
}
// fonction avec une spécification d'exception et un bloc try de fonction
int f2(std::string str) noexcept
try
{
    return std::stoi(str);
}
catch (const std::exception& e)
{
    std::cerr << "stoi() failed!\n";
    return 0;
}
// fonction supprimée, une tentative d'appel résulte en une erreur de compilation
void bar() = delete
#   if __cpp_deleted_function
    ("reason")
#   endif
;

Sortie possible :

stoi() failed!
Hello, world!
Hello, test!
Hello, again!
f2("bad"): 0
f2("42"): 42

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 tel que publié Comportement correct
CWG 135 C++98 les fonctions membres définies dans la classe
ne pouvaient pas avoir de paramètre ou retourner
sa propre classe car elle est incomplète
autorisé
CWG 332 C++98 un paramètre pouvait avoir un type void qualifié cv interdit
CWG 393 C++98 les types incluant des pointeurs/références vers
un tableau de taille inconnue ne pouvaient pas être des paramètres
ces types sont autorisés
CWG 452 C++98 la liste d'initialisation des membres ne faisait pas partie du corps de fonction elle en fait partie
CWG 577 C++98 le type dépendant void pouvait être utilisé pour
déclarer une fonction ne prenant aucun paramètre
seul void non dépendant
est autorisé
CWG 1327 C++11 les fonctions par défaut ou supprimées ne pouvaient pas
être spécifiées avec override ou final
autorisé
CWG 1355 C++11 seules les fonctions membres spéciales pouvaient être fournies par l'utilisateur étendu à toutes les fonctions
CWG 1394 C++11 les fonctions supprimées ne pouvaient avoir aucun paramètre d'un
type incomplet ou retourner un type incomplet
type incomplet autorisé
CWG 1824 C++98 la vérification de complétude sur le type de paramètre et
le type de retour d'une définition de fonction pouvait être faite
en dehors du contexte de la définition de fonction
vérification uniquement dans le
contexte de la
définition de fonction
CWG 1877 C++14 la déduction du type de retour traitait return ; comme return void ( ) ; déduit simplement le type de retour
comme void dans ce cas
CWG 2015 C++11 l'utilisation odr implicite d'une fonction
virtuelle supprimée était mal formée
ces utilisations odr sont exemptées
de l'interdiction d'utilisation
CWG 2044 C++14 la déduction du type de retour sur les fonctions retournant void
échouait si le type de retour déclaré est decltype ( auto )
mise à jour de la règle de déduction
pour gérer ce cas
CWG 2081 C++14 les redéclarations de fonction pouvaient utiliser la déduction
du type de retour même si la déclaration initiale ne le fait pas
non autorisé
CWG 2144 C++11 { } pouvait être un corps de fonction ou un initialisateur au même endroit différencié par le type
de l'identifiant du déclarateur
CWG 2145 C++98 le déclarateur dans la définition de fonction ne pouvait pas être parenthésé autorisé
CWG 2259 C++11 la règle de résolution d'ambiguïté concernant les noms de type
parenthésés ne couvrait pas les expressions lambda
couvert
CWG 2430 C++98 dans la définition d'une fonction membre dans une définition de classe,
le type de cette classe ne pouvait pas être le type de retour ou
le type de paramètre en raison de la résolution de CWG issue 1824
vérification uniquement dans le
corps de fonction
CWG 2760 C++98 le corps de fonction d'un constructeur n'incluait pas les initialisations
non spécifiées dans le corps de fonction régulier du constructeur
inclut également ces
initialisations
CWG 2831 C++20 une définition de fonction avec une clause requires
pouvait définir une fonction non template
interdit
CWG 2846 C++23 les fonctions membres à objet explicite ne pouvaient pas avoir de définitions hors-classe autorisé
CWG 2915 C++23 les paramètres d'objet explicite sans nom pouvaient avoir le type void interdit

Voir aussi

Documentation C pour Déclaration de fonctions