Значення параметру функції за замовчуванням


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

}

Обидва працюють; який стандартний спосіб і чому ?

Відповіді:


203

Якщо ви помістите декларацію у файл заголовка, а визначення - в окремий .cppфайл, а #includeзаголовок - з іншого .cppфайлу, ви зможете побачити різницю.

Припустимо:

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

Компіляція test.cppне побачить декларацію параметрів за замовчуванням і не вдасться із помилкою.

З цієї причини визначення параметра за замовчуванням зазвичай задається у декларації функції :

lib.h

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

Тоді bбуде визначено кілька разів, один раз для кожної одиниці компіляції, що включає lib.h, це правильно?
httpinterpret

@httpinterpret: в одному сенсі так, значення за замовчуванням bвизначається один раз для кожного .cpp файлу, що включає заголовок. Але це нормально, тому що у вас є лише одне оголошення Addфункції.
Грег Х'югілл

1
@httpinterpret Компілятор додасть не вказаний параметр за замовчуванням, коли генерується код абонента. Ось чому значення за замовчуванням ОБОВ'ЯЗКОВО має бути в прототипі функції, а не в реалізації функції. Параметр не визначений у значенні визначення змінної, оскільки прототип не визначає змінних.
harper

1
Цю відповідь можна було відредагувати, тому що швидкий аналіз (просто дивлячись на код і не збираючись, поки "З цієї причини") дав мені зрозуміти протилежне тому, що ви мали на увазі.
Габріель Дияволс

44

У C ++ вимоги, що пред'являються до аргументів за замовчуванням щодо їх розташування в списку параметрів, є такими:

  1. Аргумент за замовчуванням для даного параметра повинен бути вказаний не більше одного разу. Вказувати його не раз (навіть із тим самим значенням за замовчуванням) є незаконним.

  2. Параметри з аргументами за замовчуванням повинні утворювати суміжну групу в кінці списку параметрів.

Тепер, маючи це на увазі, в C ++ вам дозволяється "вирощувати" набір параметрів, які мають аргументи за замовчуванням від одного оголошення функції до іншого, до тих пір, поки вищезазначені вимоги постійно задовольняються.

Наприклад, ви можете оголосити функцію без аргументів за замовчуванням

void foo(int a, int b);

Щоб викликати цю функцію після такої декларації, вам доведеться чітко вказати обидва аргументи.

Пізніше (далі вниз) у тому ж блоці перекладу ви можете повторно оголосити його ще раз, але цього разу одним аргументом за замовчуванням

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

і з цього моменту ви можете викликати це лише одним явним аргументом.

Далі вниз ви можете знову оголосити його ще раз, додавши ще один аргумент за замовчуванням

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

і з цього моменту ви можете викликати це без явних аргументів.

Повний приклад може виглядати наступним чином

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

Що стосується коду у вашому питанні, то обидва варіанти цілком дійсні, але вони означають різні речі. Перший варіант одразу оголошує аргумент за замовчуванням для другого параметра. Другий варіант спочатку оголошує вашу функцію без аргументів за замовчуванням, а потім додає один для другого параметра.

Чистий ефект обох ваших декларацій (тобто так, як це бачиться за кодом, який слідує за другим оголошенням) абсолютно однаковий: функція має аргумент за замовчуванням для свого другого параметра. Однак якщо вам вдасться видавити якийсь код між першою та другою деклараціями, ці два варіанти будуть вести себе по-різному. У другому варіанті функція не має аргументів за замовчуванням між деклараціями, тому вам доведеться чітко вказати обидва аргументи.


Я не думаю, що ваш код, визначений void foo (int a = 1, int b), буде працювати. Потрібно мати всі необов’язкові параметри після одного необов’язкового параметра. Це синтаксична помилка (принаймні, з g ++ 4.5.3 в моїй системі).
Нілеш

@Nilesh: Як я прямо говорив вище (і в чому полягає вся суть цього прикладу), для void foo(int a = 1, int b)роботи це потрібно оголосити після void foo(int a, int b = 5) . Так, це спрацює. І ні, це не синтаксична помилка. g ++ 4.5.3 складе це ідеально добре.
ANT

Гаразд, функція приймає значення b з попереднього оголошення. Розбирання речі зараз. Дякую :-)
Нілеш

1
@Nilesh: Так, декларації аргументів за замовчуванням накопичуються в усіх попередніх деклараціях в блоці перекладу.
ANT

1
Мені подобається писати прототипи своїх функцій без змінних імен, наприклад int foo(int). Я вважаю, що можу писати int foo(int=5)ще раз, не залишаючи імен параметрів. Здається, ніхто ще не згадував про це.
Віктор Ейхуут

5

Перший спосіб віддав перевагу другому.

Це відбувається тому, що файл заголовка покаже, що параметр є необов’язковим і яким буде його значення за замовчуванням. Крім того, це забезпечить, що значення за замовчуванням буде однаковим, незалежно від реалізації відповідного .cpp-файлу.

По-друге, немає гарантії значення за замовчуванням для другого параметра. Значення за замовчуванням може змінюватися залежно від способу реалізації відповідного .cpp-файлу.


4

Аргументи за замовчуванням повинні бути вказані з першим виникненням назви функції - як правило, в прототипі функції. Якщо прототип функції опущено, оскільки визначення функції також виконує роль прототипу, тоді аргументи за замовчуванням повинні бути вказані у заголовку функції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.