Значение по умолчанию параметра функции

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

}

Оба работают; какой стандартный способ и почему ?

httpinterpret
источник

Ответы:

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
@Nilesh: Как я прямо сказал выше (и в этом весь смысл этого примера), void foo(int a = 1, int b)чтобы он работал, он должен быть объявлен после void foo(int a, int b = 5) . Да, сработает. И нет, это не синтаксическая ошибка. g ++ 4.5.3 отлично его скомпилирует.
AnT
Итак, функция принимает значение b из предыдущего объявления. Получил вещь сейчас. Спасибо :-)
Nilesh
1
@Nilesh: Да, объявления аргументов по умолчанию накапливаются во всех предыдущих объявлениях в модуле перевода.
AnT
1
Мне нравится писать прототипы функций без имен переменных, например int foo(int). Я обнаружил, что могу писать int foo(int=5)снова, опуская имена параметров. Похоже, что пока об этом никто не упомянул.
Виктор Эйджхут,
5

Первый способ предпочтительнее второго.

Это связано с тем, что в заголовочном файле будет показано, что параметр является необязательным и каким будет его значение по умолчанию. Кроме того, это гарантирует, что значение по умолчанию будет таким же, независимо от реализации соответствующего файла .cpp.

Во втором случае нет гарантии значения по умолчанию для второго параметра. Значение по умолчанию может измениться в зависимости от того, как реализован соответствующий файл .cpp.

user342264
источник
4

Аргументы по умолчанию должны быть указаны с первым вхождением имени функции - обычно в прототипе функции. Если прототип функции опущен, потому что определение функции также служит прототипом, тогда аргументы по умолчанию должны быть указаны в заголовке функции.

clyfe
источник