Valor predeterminado del parámetro de función

130

1)

int Add (int a, int b = 3);
int Add (int a, int b)
{

}

2)

int Add (int a, int b);
int Add (int a, int b = 3)
{

}

Ambos trabajan; ¿Cuál es la forma estándar y por qué ?

httpinterpret
fuente

Respuestas:

203

Si coloca la declaración en un archivo de encabezado, y la definición en un .cpparchivo separado , y #includeel encabezado de un .cpparchivo diferente , podrá ver la diferencia.

Específicamente, suponga:

lib.h

int Add(int a, int b);

lib.cpp

int Add(int a, int b = 3) {
   ...
}

test.cpp

#include "lib.h"

int main() {
    Add(4);
}

La compilación de test.cppno verá la declaración de parámetro predeterminada y fallará con un error.

Por esta razón, la definición de parámetro predeterminada generalmente se especifica en la declaración de función :

lib.h

int Add(int a, int b = 3);
Greg Hewgill
fuente
Luego bse definirá varias veces, una vez por cada unidad de compilación que incluya lib.h, ¿es así?
httpinterpret
@httpinterpret: en un sentido sí, el valor predeterminado de bse define una vez para cada .cpp archivo que incluye el encabezado. Pero está bien, porque solo tienes una declaración de la Addfunción.
Greg Hewgill
1
@httpinterpret El compilador agregará el parámetro no especificado por el parámetro predeterminado cuando se genere el código de la persona que llama. Es por eso que el valor predeterminado DEBE estar en el prototipo de la función y no en la implementación de la función. El parámetro no está definido en el sentido de definición de variable ya que el prototipo no define variables.
Harper
1
Esta respuesta se pudo editar porque un análisis rápido (solo mirar el código y no continuar hasta "Por esta razón") me hizo comprender lo contrario de lo que querías decir.
Gabriel Devillers
44

En C ++, los requisitos impuestos a los argumentos predeterminados con respecto a su ubicación en la lista de parámetros son los siguientes:

  1. El argumento predeterminado para un parámetro dado debe especificarse no más de una vez. Es ilegal especificarlo más de una vez (incluso con el mismo valor predeterminado).

  2. Los parámetros con argumentos predeterminados deben formar un grupo contiguo al final de la lista de parámetros.

Ahora, teniendo esto en cuenta, en C ++ se le permite "hacer crecer" el conjunto de parámetros que tienen argumentos predeterminados de una declaración de la función a la siguiente, siempre que los requisitos anteriores se satisfagan continuamente.

Por ejemplo, puede declarar una función sin argumentos predeterminados

void foo(int a, int b);

Para llamar a esa función después de dicha declaración, deberá especificar ambos argumentos explícitamente.

Más adelante (más abajo) en la misma unidad de traducción, puede volver a declararlo nuevamente, pero esta vez con un argumento predeterminado

void foo(int a, int b = 5);

y a partir de este momento puedes llamarlo con un solo argumento explícito.

Más abajo, puede volver a declararlo una vez más agregando un argumento predeterminado más

void foo(int a = 1, int b);

y a partir de este momento puedes llamarlo sin argumentos explícitos.

El ejemplo completo podría verse de la siguiente manera

void foo(int a, int b);

int main()
{
  foo(2, 3);

  void foo(int a, int b = 5); // redeclare
  foo(8); // OK, calls `foo(8, 5)`

  void foo(int a = 1, int b); // redeclare again
  foo(); // OK, calls `foo(1, 5)`
}

void foo(int a, int b)
{
  // ...
}

En cuanto al código en su pregunta, ambas variantes son perfectamente válidas, pero significan cosas diferentes. La primera variante declara un argumento predeterminado para el segundo parámetro de inmediato. La segunda variante declara inicialmente su función sin argumentos predeterminados y luego agrega uno para el segundo parámetro.

El efecto neto de ambas declaraciones (es decir, la forma en que lo ve el código que sigue a la segunda declaración) es exactamente el mismo: la función tiene un argumento predeterminado para su segundo parámetro. Sin embargo, si logras exprimir algo de código entre la primera y la segunda declaración, estas dos variantes se comportarán de manera diferente. En la segunda variante, la función no tiene argumentos predeterminados entre las declaraciones, por lo que deberá especificar ambos argumentos explícitamente.

Hormiga
fuente
No creo que su código definido void foo (int a = 1, int b) funcione. Debe tener todos los parámetros opcionales después de un parámetro opcional. Es un error de sintaxis (al menos con g ++ 4.5.3 en mi sistema).
Nilesh
@Nilesh: Como dije explícitamente anteriormente (y cuál es el objetivo de este ejemplo) para void foo(int a = 1, int b)que funcione, debe declararse después void foo(int a, int b = 5) . Sí, funcionará Y no, no es un error de sintaxis. g ++ 4.5.3 lo compilará perfectamente bien.
ANT
Bien, entonces la función toma el valor de b de la declaración anterior. Obteniendo la cosa ahora. Gracias :-)
Nilesh
1
@Nilesh: Sí, las declaraciones de argumento predeterminadas se acumulan en todas las declaraciones anteriores en la unidad de traducción.
ANT
1
Me gusta escribir mis prototipos de funciones sin nombres de variables, como int foo(int). Me parece que puedo int foo(int=5)volver a escribir , dejando de lado los nombres de los parámetros. Nadie parece haber mencionado eso todavía.
Victor Eijkhout
5

La primera forma sería preferible a la segunda.

Esto se debe a que el archivo de encabezado mostrará que el parámetro es opcional y cuál será su valor predeterminado. Además, esto garantizará que el valor predeterminado sea el mismo, sin importar la implementación del archivo .cpp correspondiente.

En la segunda forma, no hay garantía de un valor predeterminado para el segundo parámetro. El valor predeterminado podría cambiar, dependiendo de cómo se implemente el archivo .cpp correspondiente.

usuario342264
fuente
4

Los argumentos predeterminados deben especificarse con la primera aparición del nombre de la función, generalmente, en el prototipo de la función. Si se omite el prototipo de la función porque la definición de la función también sirve como prototipo, entonces los argumentos predeterminados deben especificarse en el encabezado de la función.

Clyfe
fuente