Namespaces
Variants

Default arguments

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

Permet d'appeler une fonction sans fournir un ou plusieurs arguments de fin.

Indiqué par l'utilisation de la syntaxe suivante pour un paramètre dans la parameter-list d'une déclaration de fonction .

attr  (optionnel) decl-specifier-seq declarator = initializer (1)
attr  (optionnel) decl-specifier-seq abstract-declarator  (optionnel) = initializer (2)

Les arguments par défaut sont utilisés à la place des arguments de fin manquants dans un appel de fonction :

void point(int x = 3, int y = 4);
point(1, 2); // appelle point(1, 2)
point(1);    // appelle point(1, 4)
point();     // appelle point(3, 4)

Dans une déclaration de fonction, après un paramètre avec un argument par défaut, tous les paramètres suivants doivent :

  • possède un argument par défaut fourni dans cette déclaration ou une déclaration antérieure de la même portée :
int x(int = 1, int); // Erreur : seuls les paramètres de fin peuvent avoir des arguments par défaut
                     //        (en supposant qu'il n'y a pas de déclaration précédente de « x »)
void f(int n, int k = 1);
void f(int n = 0, int k); // OK : l'argument par défaut de « k » est fourni par
                          // la déclaration précédente dans la même portée
void g(int, int = 7);
void h()
{
    void g(int = 1, int); // Erreur : pas la même portée
}
  • ...sauf si le paramètre a été développé à partir d'un pack de paramètres :
template<class... T>
struct C { void f(int n = 0, T...); };
C<int> c;  // OK; instantiates declaration void C::f(int n = 0, int)
  • ou être un pack de paramètres de fonction :
template<class... T>
void h(int i = 0, T... args); // OK
(depuis C++11)

L'ellipse n'est pas un paramètre, et peut donc suivre un paramètre avec un argument par défaut :

int g(int n = 0, ...); // Correct

Les arguments par défaut sont uniquement autorisés dans les listes de paramètres des déclarations de fonction et des expressions lambda , (depuis C++11) et ne sont pas autorisés dans les déclarations de pointeurs vers des fonctions, de références vers des fonctions, ou dans les déclarations typedef . Les listes de paramètres de template utilisent une syntaxe similaire pour leurs arguments de template par défaut .

Pour les fonctions non template, des arguments par défaut peuvent être ajoutés à une fonction déjà déclarée si la fonction est redéclarée dans la même portée. Au point d'un appel de fonction, les arguments par défaut sont une union des arguments par défaut fournis dans toutes les déclarations visibles de la fonction. Une redéclaration ne peut pas introduire un argument par défaut pour un paramètre pour lequel un argument par défaut est déjà visible (même si la valeur est identique). Une redéclaration dans une portée interne n'acquiert pas les arguments par défaut des portées externes.

void f(int, int);     // #1
void f(int, int = 7); // #2 OK : ajoute un argument par défaut
void h()
{
    f(3); // #1 et #2 sont dans la portée ; effectue un appel à f(3,7)
    void f(int = 1, int); // Erreur : l'argument par défaut du second
                          // paramètre n'est pas acquis depuis les portées externes
}
void m()
{ // nouvelle portée commence
    void f(int, int); // déclaration de portée interne ; n'a pas d'argument par défaut.
    f(4); // Erreur : arguments insuffisants pour appeler f(int, int)
    void f(int, int = 6);
    f(4); // OK : appelle f(4, 6) ;
    void f(int, int = 6); // Erreur : le second paramètre a déjà un
                          // argument par défaut (même si les valeurs sont identiques)
}
void f(int = 1, int); // #3 OK, ajoute un argument par défaut à #2
void n()
{ // nouvelle portée commence
    f(); // #1, #2 et #3 sont dans la portée : appelle f(1, 7) ;
}

Si une fonction inline est déclarée dans différentes unités de traduction, les ensembles accumulés d'arguments par défaut doivent être identiques à la fin de chaque unité de traduction.

Si une fonction non inline est déclarée dans la même portée de namespace dans différentes unités de traduction, les arguments par défaut correspondants doivent être identiques s'ils sont présents (mais certains arguments par défaut peuvent être absents dans certaines TU).

(since C++20)

Si une déclaration friend spécifie un argument par défaut, elle doit être une définition de fonction friend, et aucune autre déclaration de cette fonction n'est autorisée dans l'unité de traduction.

La using-declarations transmet l'ensemble des arguments par défaut connus, et si d'autres arguments par défaut sont ajoutés ultérieurement dans l'espace de noms de la fonction, ces arguments par défaut sont également visibles partout où la using-declaration est visible :

namespace N
{
    void f(int, int = 1);
}
using N::f;
void g()
{
    f(7); // appelle f(7, 1);
    f();  // erreur
}
namespace N
{
    void f(int = 2, int);
}
void h()
{
    f();  // appelle f(2, 1);
}

Les noms utilisés dans les arguments par défaut sont recherchés, vérifiés pour l'accessibilité , et liés au point de déclaration, mais sont exécutés au point de l'appel de fonction :

int a = 1;
int f(int);
int g(int x = f(a)); // la recherche de f trouve ::f, la recherche de a trouve ::a
                     // la valeur de ::a, qui est 1 à ce stade, n'est pas utilisée
void h()
{
    a = 2; // modifie la valeur de ::a
    {
        int a = 3;
        g(); // appelle f(2), puis appelle g() avec le résultat
    }
}

Pour une fonction membre d'une classe non template , les arguments par défaut sont autorisés dans la définition hors-classe, et sont combinés avec les arguments par défaut fournis par la déclaration dans le corps de la classe. Si ces arguments par défaut hors-classe transforment une fonction membre en constructeur par défaut ou en opérateur d'assignation de copie /déplacement (depuis C++11) (ce qui rend l'appel ambigu), le programme est mal formé. Pour les fonctions membres des classes template, tous les arguments par défaut doivent être fournis dans la déclaration initiale de la fonction membre.

class C
{
    void f(int i = 3);
    void g(int i, int j = 99);
    C(int arg); // constructeur non par défaut
};
void C::f(int i = 3) {}         // erreur : argument par défaut déjà
                                // spécifié dans la portée de la classe
void C::g(int i = 88, int j) {} // OK : dans cette unité de traduction,
                                // C::g peut être appelé sans argument
C::C(int arg = 1) {}            // Erreur : transforme ceci en constructeur par défaut

Les surchargeurs des fonctions virtual n'acquièrent pas les arguments par défaut des déclarations de la classe de base, et lorsque l'appel de fonction virtuelle est effectué, les arguments par défaut sont déterminés en fonction du type statique de l'objet (note : cela peut être évité avec le patron non-virtual interface ).

struct Base
{
    virtual void f(int a = 7);
};
struct Derived : Base
{
    void f(int a) override;
};
void m()
{
    Derived d;
    Base& b = d;
    b.f(); // OK : appelle Derived::f(7)
    d.f(); // Erreur : pas d'argument par défaut
}

Les variables locales ne sont pas autorisées dans les arguments par défaut, sauf si elles sont non évaluées :

void f()
{
    int n = 1;
    extern void g(int x = n); // erreur : une variable locale ne peut pas être un argument par défaut
    extern void h(int x = sizeof n); // OK depuis CWG 2082
}

Le pointeur this n'est pas autorisé dans les arguments par défaut :

class A
{
    void f(A* p = this) {} // erreur : ceci n'est pas autorisé
};

Les membres non statiques de classe ne sont pas autorisés dans les arguments par défaut (même s'ils ne sont pas évalués), sauf lorsqu'ils sont utilisés pour former un pointeur-sur-membre ou dans une expression d'accès à un membre :

int b;
class X
{
    int a;
    int mem1(int i = a); // erreur : un membre non statique ne peut pas être utilisé
    int mem2(int i = b); // OK : la recherche trouve X::b, le membre statique
    int mem3(int X::* i = &X::a); // OK : un membre non statique peut être utilisé
    int mem4(int i = x.a); // OK : dans une expression d'accès membre
    static X x;
    static int b;
};

Un argument par défaut est évalué chaque fois que la fonction est appelée sans argument pour le paramètre correspondant. Les paramètres de fonction ne sont pas autorisés dans les arguments par défaut, sauf s'ils sont non évalués . Notez que les paramètres qui apparaissent plus tôt dans la liste des paramètres sont dans la portée :

int a;
int f(int a, int b = a); // Erreur : le paramètre a utilisé dans un argument par défaut
int g(int a, int b = sizeof a); // Erreur jusqu'à la résolution de CWG 2082
                                // OK après résolution : l'utilisation dans un contexte non évalué est acceptable

Les arguments par défaut ne font pas partie du type de fonction :

int f(int = 0);
void h()
{
    int j = f(1);
    int k = f(); // appelle f(0);
}
int (*p1)(int) = &f;
int (*p2)()    = &f; // Erreur : le type de f est int(int)

Les fonctions d'opérateur autres que l' opérateur d'appel de fonction et l' opérateur d'indice (depuis C++23) ne peuvent pas avoir d'arguments par défaut :

class C
{
    int operator++(int i = 0); // mal formé
    int operator[](int j = 0); // OK depuis C++23
    int operator()(int k = 0); // OK
};

Les paramètres d'objet explicites ne peuvent pas avoir d'arguments par défaut :

struct S { void f(this const S& = S{}); }; // ill-formed
(depuis C++23)

Note

Des espaces peuvent être nécessaires pour éviter un jeton d'assignation composé si le nom du paramètre est absent (voir maximal munch ).

void f1(int*=0);         // Erreur, « *= » est inattendu ici
void g1(const int&=0);   // Erreur, « &= » est inattendu ici
void f2(int* = 0);       // OK
void g2(const int& = 0); // OK
void h(int&&=0);         // OK même sans espaces, « && » est un jeton ici

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 217 C++98 un argument par défaut pouvait être ajouté à une fonction membre
non-template d'un modèle de classe
interdit
CWG 1344 C++98 les arguments par défaut ajoutés dans la définition hors-classe
d'une fonction membre pouvaient la transformer en fonction membre spéciale
interdit
CWG 1716 C++98 les arguments par défaut étaient évalués à chaque appel de la fonction,
même si l'appelant fournissait les arguments
évalués uniquement si aucun
argument n'est fourni pour le
paramètre correspondant
CWG 2082 C++98 les arguments par défaut ne pouvaient pas utiliser les variables locales
et les paramètres précédents dans un contexte non évalué
utilisation en contexte non évalué
autorisée
CWG 2233 C++11 les paramètres développés à partir de packs de paramètres ne pouvaient
pas apparaître après des paramètres avec arguments par défaut
autorisé
CWG 2683 C++98 les définitions hors-classe des fonctions membres des classes
imbriquées des modèles de classe pouvaient avoir des arguments par défaut
interdit