Valor padrão do parâmetro da função

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 trabalham; qual é o caminho padrão e por quê ?

httpinterpret
fonte

Respostas:

203

Se você colocar a declaração em um arquivo de cabeçalho, a definição em um .cpparquivo separado e #includeo cabeçalho de um .cpparquivo diferente , poderá ver a diferença.

Especificamente, suponha:

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);
}

A compilação de test.cppnão verá a declaração de parâmetro padrão e falhará com um erro.

Por esse motivo, a definição de parâmetro padrão geralmente é especificada na declaração da função :

lib.h

int Add(int a, int b = 3);
Greg Hewgill
fonte
Em seguida b, será definido várias vezes, uma vez para cada unidade de compilação que inclui lib.h, certo?
Httpinterpret 16/05/10
@httpinterpret: em certo sentido, sim, o valor padrão de bé definido uma vez para cada .cpp arquivo que inclui o cabeçalho. Mas tudo bem, porque você só tem uma declaração da Addfunção.
Greg Hewgill
1
@httpinterpret O compilador adicionará o parâmetro não especificado pelo parâmetro padrão quando o código do chamador for gerado. É por isso que o valor padrão DEVE estar no protótipo da função e não na implementação da função. O parâmetro não é definido no sentido de definição de variável, pois o protótipo não define variáveis.
Harper
1
Essa resposta pode ser editada porque uma análise rápida (apenas olhando o código e não indo até "Por esse motivo") me fez entender o oposto do que você quis dizer.
Gabriel Devillers
44

Em C ++, os requisitos impostos aos argumentos padrão em relação à sua localização na lista de parâmetros são os seguintes:

  1. O argumento padrão para um determinado parâmetro deve ser especificado não mais que uma vez. Especificá-lo mais de uma vez (mesmo com o mesmo valor padrão) é ilegal.

  2. Parâmetros com argumentos padrão precisam formar um grupo contíguo no final da lista de parâmetros.

Agora, mantendo isso em mente, em C ++ você pode "aumentar" o conjunto de parâmetros que possuem argumentos padrão de uma declaração da função para a próxima, desde que os requisitos acima sejam continuamente satisfeitos.

Por exemplo, você pode declarar uma função sem argumentos padrão

void foo(int a, int b);

Para chamar essa função após essa declaração, você precisará especificar ambos os argumentos explicitamente.

Posteriormente (mais abaixo) na mesma unidade de conversão, você pode declarar novamente, mas desta vez com um argumento padrão

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

e a partir daí você pode chamá-lo com apenas um argumento explícito.

Mais abaixo, você pode declará-lo novamente, adicionando mais um argumento padrão

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

e a partir deste ponto você pode chamá-lo sem argumentos explícitos.

O exemplo completo pode ter a seguinte aparência

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)
{
  // ...
}

Quanto ao código em sua pergunta, ambas as variantes são perfeitamente válidas, mas significam coisas diferentes. A primeira variante declara um argumento padrão para o segundo parâmetro imediatamente. A segunda variante inicialmente declara sua função sem argumentos padrão e, em seguida, adiciona uma para o segundo parâmetro.

O efeito líquido de ambas as suas declarações (ou seja, a maneira como é visto pelo código que segue a segunda declaração) é exatamente o mesmo: a função possui argumento padrão para o seu segundo parâmetro. No entanto, se você conseguir espremer algum código entre a primeira e a segunda declarações, essas duas variantes se comportarão de maneira diferente. Na segunda variante, a função não possui argumentos padrão entre as declarações, portanto, você precisará especificar ambos os argumentos explicitamente.

Formiga
fonte
Eu não acho que seu código definido void foo (int a = 1, int b) funcionaria. Você precisa ter todos os parâmetros opcionais após um parâmetro opcional. É um erro de sintaxe (pelo menos com o g ++ 4.5.3 no meu sistema).
Nilesh
@ Nilesh: Como eu disse explicitamente acima (e qual é o objetivo deste exemplo) para void foo(int a = 1, int b)funcionar, ele deve ser declarado depois void foo(int a, int b = 5) . Sim, vai funcionar. E não, não é um erro de sintaxe. O g ++ 4.5.3 o compilará perfeitamente.
AnT
Ok, então a função assume o valor de b da declaração anterior. Entendendo a coisa agora. Obrigado :-)
Nilesh
1
@Nilesh: Sim, as declarações de argumento padrão são acumuladas em todas as declarações anteriores na unidade de tradução.
AnT
1
Eu gosto de escrever meus protótipos de função sem nomes de variáveis, como int foo(int). Acho que posso escrever int foo(int=5)novamente, deixando de fora os nomes dos parâmetros. Ninguém parece ter mencionado isso ainda.
Victor Eijkhout 14/02
5

A primeira maneira seria preferida à segunda.

Isso ocorre porque o arquivo de cabeçalho mostrará que o parâmetro é opcional e qual será o valor padrão. Além disso, isso garantirá que o valor padrão seja o mesmo, independentemente da implementação do arquivo .cpp correspondente.

Na segunda maneira, não há garantia de um valor padrão para o segundo parâmetro. O valor padrão pode mudar, dependendo de como o arquivo .cpp correspondente é implementado.

user342264
fonte
4

Os argumentos padrão devem ser especificados com a primeira ocorrência do nome da função - normalmente, no protótipo da função. Se o protótipo da função for omitido porque a definição da função também serve como protótipo, os argumentos padrão deverão ser especificados no cabeçalho da função.

clyfe
fonte