Function declaration
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 )
| 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 | - |
|
||||||
| 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 retourSi 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
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)
|
(2) | (depuis C++23) | |||||||
attr
(optionnel)
decl-specifier-seq
declarator
=
initializer
|
(3) | ||||||||
| attr (optionnel) decl-specifier-seq abstract-declarator (optionnel) | (4) | ||||||||
|
attr
(optionnel)
|
(5) | (depuis C++23) | |||||||
attr
(optionnel)
decl-specifier-seq
abstract-declarator
(optionnel)
=
initializer
|
(6) | ||||||||
void
|
(7) | ||||||||
` 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.
| 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 :
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]);
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 :
- 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 .
-
Après avoir déterminé le type de chaque paramètre, tout paramètre de type « tableau de
T» ou de type fonctionTest ajusté en « pointeur versT». - 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.
- 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
» :
|
(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) retournantT».
|
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
|
(depuis C++11) |
|
(depuis C++17) |
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 :
- le type de fonction pour une fonction membre non statique ,
- le type de fonction vers lequel pointe un pointeur sur membre,
- le type de fonction de plus haut niveau d'une déclaration typedef ou d'une déclaration d'alias (depuis C++11) ,
- le type-id dans l'argument par défaut d'un paramètre de template de type , ou
- le type-id d'un argument de template pour un paramètre de template de type.
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
|
(depuis C++11) |
|
(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) | |||||||
| 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) | |||||||
| 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 :
- Pour tous les membres de données non statiques dont les identifiants sont absents dans la liste d'initialisation des membres du constructeur, les initialiseurs par défaut des membres ou (depuis C++11) initialisations par défaut sont utilisés pour initialiser les sous-objets membres correspondants.
- Pour toutes les classes de base dont les noms de type sont absents dans la liste d'initialisation des membres du constructeur, les initialisations par défaut sont utilisées pour initialiser les sous-objets de classe de base correspondants.
|
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éfautSi 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
Si le type de
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éesSi 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 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'utilisateurUne 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
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
Exécuter ce code
Sortie possible : Foo Bar Pub ~Bar |
(depuis C++11) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Spécificateurs de contrat de fonctionLes 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 :
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.
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éconditionUne 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); } ` 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 postconditionUne 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
Si une déclaration
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
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
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
|