Domyślna wartość parametru funkcji

133

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)
{

}

Obie działają; jaki jest standardowy sposób i dlaczego ?

httpinterpret
źródło

Odpowiedzi:

204

Jeśli umieścisz deklarację w pliku nagłówkowym, a definicję w osobnym .cpppliku, a #includenagłówek z innego .cpppliku, zobaczysz różnicę.

W szczególności przypuśćmy:

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

Kompilacja test.cppnie zobaczy domyślnej deklaracji parametru i zakończy się niepowodzeniem z błędem.

Z tego powodu domyślna definicja parametru jest zwykle podawana w deklaracji funkcji :

lib.h

int Add(int a, int b = 3);
Greg Hewgill
źródło
Wtedy bzostanie zdefiniowany wiele razy, raz dla każdej jednostki kompilacji, która zawiera lib.h, czy to prawda?
httpinterpret
@httpinterpret: w pewnym sensie tak, domyślna wartość bjest definiowana raz dla każdego .cpp pliku zawierającego nagłówek. Ale to w porządku, ponieważ masz tylko jedną deklarację Addfunkcji.
Greg Hewgill
1
@httpinterpret Kompilator doda nieokreślony parametr przez parametr domyślny, gdy kod wywołujący zostanie wygenerowany. Dlatego wartość domyślna MUSI znajdować się w prototypie funkcji, a nie w implementacji funkcji. Parametr nie jest zdefiniowany w sensie definicji zmiennej, ponieważ prototyp nie definiuje zmiennych.
harper
1
Ta odpowiedź mogłaby zostać zmieniona, ponieważ szybkie analizowanie (samo spojrzenie na kod i nie przechodzenie do „Z tego powodu”) pozwoliło mi zrozumieć przeciwieństwo tego, co miałeś na myśli.
Gabriel Devillers
44

W C ++ wymagania stawiane argumentom domyślnym w zakresie ich umiejscowienia na liście parametrów są następujące:

  1. Domyślny argument dla danego parametru należy podać nie więcej niż raz. Podanie go więcej niż raz (nawet z tą samą wartością domyślną) jest niedozwolone.

  2. Parametry z domyślnymi argumentami muszą tworzyć ciągłą grupę na końcu listy parametrów.

Mając to na uwadze, w C ++ możesz „powiększać” zestaw parametrów, które mają domyślne argumenty z jednej deklaracji funkcji do następnej, o ile powyższe wymagania są stale spełniane.

Na przykład możesz zadeklarować funkcję bez domyślnych argumentów

void foo(int a, int b);

Aby wywołać tę funkcję po takiej deklaracji, musisz jawnie określić oba argumenty.

Później (niżej) w tej samej jednostce tłumaczeniowej możesz ponownie zadeklarować ją ponownie, ale tym razem z jednym domyślnym argumentem

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

i od tego momentu możesz to wywołać za pomocą tylko jednego wyraźnego argumentu.

Dalej możesz zadeklarować go ponownie, dodając jeszcze jeden domyślny argument

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

i od tego momentu możesz go wywoływać bez wyraźnych argumentów.

Pełny przykład może wyglądać następująco

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

Jeśli chodzi o kod w twoim pytaniu, oba warianty są całkowicie prawidłowe, ale mają różne znaczenie. Pierwszy wariant od razu deklaruje domyślny argument dla drugiego parametru. Drugi wariant początkowo deklaruje twoją funkcję bez domyślnych argumentów, a następnie dodaje jeden dla drugiego parametru.

Efekt netto obu deklaracji (tj. Sposób, w jaki jest to postrzegane przez kod następujący po drugiej deklaracji) jest dokładnie taki sam: funkcja ma domyślny argument dla drugiego parametru. Jeśli jednak uda Ci się wcisnąć jakiś kod między pierwszą a drugą deklaracją, te dwa warianty będą się zachowywać inaczej. W drugim wariancie funkcja nie ma domyślnych argumentów między deklaracjami, więc będziesz musiał jawnie określić oba argumenty.

Mrówka
źródło
Nie sądzę, aby twój kod zdefiniowany jako void foo (int a = 1, int b) działał. Musisz mieć wszystkie parametry opcjonalne po jednym parametrze opcjonalnym. To błąd składni (przynajmniej z g ++ 4.5.3 w moim systemie).
Nilesh
@Nilesh: Jak już wyraźnie powiedziałem powyżej (i jest to cały punkt tego przykładu), void foo(int a = 1, int b)aby zadziałało, musi zostać zadeklarowane później void foo(int a, int b = 5) . Tak, to zadziała. I nie, to nie jest błąd składniowy. g ++ 4.5.3 skompiluje to doskonale.
AnT
OK, więc funkcja przyjmuje wartość b z poprzedniej deklaracji. Dostaję to teraz. Dzięki :-)
Nilesh
1
@Nilesh: Tak, domyślne deklaracje argumentów są gromadzone we wszystkich poprzednich deklaracjach w jednostce tłumaczeniowej.
AnT
1
Lubię pisać moje prototypy funkcji bez nazw zmiennych, na przykład int foo(int). Stwierdzam, że mogę pisać int foo(int=5)ponownie, pomijając nazwy parametrów. Jak dotąd nikt o tym nie wspomniał.
Victor Eijkhout
5

Pierwsza metoda byłaby lepsza od drugiej.

Dzieje się tak, ponieważ plik nagłówkowy pokaże, że parametr jest opcjonalny i jaka będzie jego wartość domyślna. Dodatkowo zapewni to, że wartość domyślna będzie taka sama, niezależnie od implementacji odpowiedniego pliku .cpp.

W drugim przypadku nie ma gwarancji wartości domyślnej drugiego parametru. Wartość domyślna może się zmienić w zależności od tego, jak zaimplementowano odpowiedni plik .cpp.

user342264
źródło
4

Domyślne argumenty należy określić przy pierwszym wystąpieniu nazwy funkcji - zwykle w prototypie funkcji. Jeśli prototyp funkcji zostanie pominięty, ponieważ definicja funkcji służy również jako prototyp, wówczas domyślne argumenty powinny zostać określone w nagłówku funkcji.

clyfe
źródło