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

Permite llamar a una función sin proporcionar uno o más argumentos finales.

Indicado mediante el uso de la siguiente sintaxis para un parámetro en la parameter-list de una function declaration .

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

Los argumentos predeterminados se utilizan en lugar de los argumentos finales faltantes en una llamada de función:

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

En una declaración de función, después de un parámetro con un argumento predeterminado, todos los parámetros subsiguientes deben:

  • tiene un argumento predeterminado proporcionado en esta o una declaración anterior del mismo ámbito:
int x(int = 1, int); // Error: solo los parámetros finales pueden tener argumentos predeterminados
                     //        (asumiendo que no hay una declaración previa de "x")
void f(int n, int k = 1);
void f(int n = 0, int k); // OK: el argumento predeterminado de "k" es proporcionado por
                          // la declaración previa en el mismo ámbito
void g(int, int = 7);
void h()
{
    void g(int = 1, int); // Error: no es el mismo ámbito
}
  • ...a menos que el parámetro se expandiera desde un paquete de parámetros:
template<class... T>
struct C { void f(int n = 0, T...); };
C<int> c;  // OK; instantiates declaration void C::f(int n = 0, int)
  • o sea un paquete de parámetros de función:
template<class... T>
void h(int i = 0, T... args); // OK
(desde C++11)

Los puntos suspensivos no son un parámetro, por lo que pueden seguir a un parámetro con un argumento predeterminado:

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

Los argumentos predeterminados solo están permitidos en las listas de parámetros de declaraciones de función y expresiones lambda , (desde C++11) y no están permitidos en las declaraciones de punteros a funciones, referencias a funciones, o en typedef declarations. Las listas de parámetros de plantilla utilizan sintaxis similar para sus argumentos de plantilla predeterminados .

Para funciones no plantilla, los argumentos predeterminados pueden añadirse a una función que ya fue declarada si la función se redeclara en el mismo ámbito. En el punto de una llamada a función, los argumentos predeterminados son una unión de los argumentos predeterminados proporcionados en todas las declaraciones visibles para la función. Una redeclaración no puede introducir un argumento predeterminado para un parámetro para el cual ya es visible un argumento predeterminado (incluso si el valor es el mismo). Una redeclaración en un ámbito interno no adquiere los argumentos predeterminados de ámbitos externos.

void f(int, int);     // #1
void f(int, int = 7); // #2 OK: agrega un argumento por defecto
void h()
{
    f(3); // #1 y #2 están en el ámbito; realiza una llamada a f(3,7)
    void f(int = 1, int); // Error: el argumento por defecto del segundo
                          // parámetro no se adquiere de ámbitos externos
}
void m()
{ // comienza un nuevo ámbito
    void f(int, int); // declaración en ámbito interno; no tiene argumento por defecto.
    f(4); // Error: argumentos insuficientes para llamar a f(int, int)
    void f(int, int = 6);
    f(4); // OK: llama a f(4, 6);
    void f(int, int = 6); // Error: el segundo parámetro ya tiene un
                          // argumento por defecto (incluso si los valores son iguales)
}
void f(int = 1, int); // #3 OK, agrega un argumento por defecto a #2
void n()
{ // comienza un nuevo ámbito
    f(); // #1, #2 y #3 están en el ámbito: llama a f(1, 7);
}

Si una función inline se declara en diferentes unidades de traducción, los conjuntos acumulados de argumentos predeterminados deben ser iguales al final de cada unidad de traducción.

Si una función no inline se declara en el mismo ámbito de espacio de nombres en diferentes unidades de traducción, los argumentos predeterminados correspondientes deben ser los mismos si están presentes (pero algunos argumentos predeterminados pueden estar ausentes en algunas TU).

(desde C++20)

Si una declaración friend especifica un argumento predeterminado, debe ser una definición de función friend, y no se permiten otras declaraciones de esta función en la unidad de traducción.

La using-declarations transfiere el conjunto de argumentos predeterminados conocidos, y si se agregan más argumentos predeterminados posteriormente al namespace de la función, esos argumentos predeterminados también son visibles en cualquier lugar donde la using-declaration sea visible:

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

Los nombres utilizados en los argumentos predeterminados se buscan, se verifican para accesibilidad , y se vinculan en el punto de declaración, pero se ejecutan en el punto de la llamada a la función:

int a = 1;
int f(int);
int g(int x = f(a)); // la búsqueda de f encuentra ::f, la búsqueda de a encuentra ::a
                     // el valor de ::a, que es 1 en este punto, no se utiliza
void h()
{
    a = 2; // cambia el valor de ::a
    {
        int a = 3;
        g(); // llama a f(2), luego llama a g() con el resultado
    }
}

Para una función miembro de una clase no plantilla , los argumentos por defecto están permitidos en la definición fuera de la clase, y se combinan con los argumentos por defecto proporcionados por la declaración dentro del cuerpo de la clase. Si estos argumentos por defecto fuera de la clase convirtieran una función miembro en un constructor por defecto o operador de asignación de copia /movimiento (desde C++11) (lo que hace que la llamada sea ambigua), el programa está mal formado. Para funciones miembro de clases plantilla, todos los argumentos por defecto deben proporcionarse en la declaración inicial de la función miembro.

class C
{
    void f(int i = 3);
    void g(int i, int j = 99);
    C(int arg); // constructor no predeterminado
};
void C::f(int i = 3) {}         // error: argumento predeterminado ya
                                // especificado en el ámbito de la clase
void C::g(int i = 88, int j) {} // OK: en esta unidad de traducción,
                                // C::g puede ser llamado sin argumento
C::C(int arg = 1) {}            // Error: convierte esto en un constructor predeterminado

Los redefinidores de virtual functions no adquieren los argumentos por defecto de las declaraciones de la clase base, y cuando se realiza la llamada a la función virtual, los argumentos por defecto se deciden basándose en el tipo estático del objeto (nota: esto puede evitarse con el patrón 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: llama a Derived::f(7)
    d.f(); // Error: no hay argumento predeterminado
}

Las variables locales no están permitidas en argumentos predeterminados a menos que sean no evaluadas :

void f()
{
    int n = 1;
    extern void g(int x = n); // error: la variable local no puede ser un argumento por defecto
    extern void h(int x = sizeof n); // OK según CWG 2082
}

El puntero this no está permitido en argumentos por defecto:

class A
{
    void f(A* p = this) {} // error: no se permite this
};

Los miembros no estáticos de clase no están permitidos en argumentos por defecto (incluso si no son evaluados), excepto cuando se utilizan para formar un puntero-a-miembro o en una expresión de acceso a miembro:

int b;
class X
{
    int a;
    int mem1(int i = a); // error: no se puede usar miembro no estático
    int mem2(int i = b); // OK: la búsqueda encuentra X::b, el miembro estático
    int mem3(int X::* i = &X::a); // OK: se puede usar miembro no estático
    int mem4(int i = x.a); // OK: en una expresión de acceso a miembro
    static X x;
    static int b;
};

Un argumento predeterminado se evalúa cada vez que se llama a la función sin argumento para el parámetro correspondiente. No se permiten parámetros de función en argumentos predeterminados, excepto si son no evaluados . Nótese que los parámetros que aparecen antes en la lista de parámetros están en ámbito :

int a;
int f(int a, int b = a); // Error: el parámetro a utilizado en un argumento por defecto
int g(int a, int b = sizeof a); // Error hasta la resolución de CWG 2082
                                // OK después de la resolución: el uso en contexto no evaluado es aceptable

Los argumentos predeterminados no forman parte del tipo de función:

int f(int = 0);
void h()
{
    int j = f(1);
    int k = f(); // llama a f(0);
}
int (*p1)(int) = &f;
int (*p2)()    = &f; // Error: el tipo de f es int(int)

Las funciones de operador distintas de la función de llamada a función y el operador de subíndice (desde C++23) no pueden tener argumentos predeterminados:

class C
{
    int operator++(int i = 0); // incorrecto
    int operator[](int j = 0); // Válido desde C++23
    int operator()(int k = 0); // Válido
};

Parámetros de objeto explícitos no pueden tener argumentos predeterminados:

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

Nota

Puede que sean necesarios espacios para evitar un token de asignación compuesta si el nombre del parámetro está ausente (consulte maximal munch ).

void f1(int*=0);         // Error, “*=” es inesperado aquí
void g1(const int&=0);   // Error, “&=” es inesperado aquí
void f2(int* = 0);       // OK
void g2(const int& = 0); // OK
void h(int&&=0);         // OK incluso sin espacios, “&&” es un token aquí

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 217 C++98 se podía agregar un argumento predeterminado a una función miembro no
plantilla de una plantilla de clase
prohibido
CWG 1344 C++98 los argumentos predeterminados agregados en la definición externa de una
función miembro podían cambiarla a una función miembro especial
prohibido
CWG 1716 C++98 los argumentos predeterminados se evaluaban cada vez que la función
es llamada, incluso si el llamador proporcionaba los argumentos
evaluados solo si no se
proporciona ningún argumento para el
parámetro correspondiente
CWG 2082 C++98 los argumentos predeterminados tenían prohibido usar variables locales
y parámetros precedentes en contexto no evaluado
uso en contexto no evaluado
permitido
CWG 2233 C++11 los parámetros expandidos desde paquetes de parámetros no
podían aparecer después de parámetros con argumentos predeterminados
permitido
CWG 2683 C++98 las definiciones externas de las funciones miembro de clases
anidadas de plantillas de clase podían tener argumentos predeterminados
prohibido