Default arguments
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 }
template<class... T> struct C { void f(int n = 0, T...); }; C<int> c; // OK; instantiates declaration void C::f(int n = 0, int)
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 |