Где установить значение параметра по умолчанию на С++?

Какое место для значения параметра по умолчанию? Просто в определении функции или декларации или в обоих местах?

Ответ 1

Значения параметров по умолчанию должны появляться в объявлении, поскольку это единственное, что видит вызывающий.

РЕДАКТИРОВАТЬ: Как указывают другие, вы можете иметь аргумент в определении, но я бы посоветовал написать весь код, как будто это было неверно.

Ответ 2

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

Ответ 3

C++ помещает логику параметров по умолчанию в сторону вызова, это означает, что если выражение значения по умолчанию не может быть вычислено из места вызова, то значение по умолчанию не может быть использовано.

Другие блоки компиляции обычно просто включают объявление, поэтому выражения значений по умолчанию, помещенные в определение, могут использоваться только в самой определяющей единице компиляции (и после определения, т.е. после того, как компилятор увидит выражения значений по умолчанию).

Самое полезное место находится в объявлении (.h), чтобы его увидели все пользователи.

Некоторые люди любят добавлять выражения значения по умолчанию в реализацию (как комментарий):

void foo(int x = 42,
         int y = 21);

void foo(int x /* = 42 */,
         int y /* = 21 */)
{
   ...
}

Однако это означает дублирование и добавит возможность не синхронизировать комментарий с кодом (что хуже, чем незакомментированный код? Код с вводящими в заблуждение комментариями!).

Ответ 4

Хотя это "старый" поток, я все же хотел бы добавить к нему следующее:

Я испытал следующий случай:

  • В файле заголовка класса я имел
int SetI2cSlaveAddress( UCHAR addr, bool force );
  • В исходном файле этого класса у меня был
int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force = false )
{
   ...
}

Как можно видеть, я поместил значение по умолчанию параметра "force" в исходный файл класса, а не в заголовочный файл класса.

Затем я использовал эту функцию в производном классе следующим образом (производный класс унаследовал базовый класс публичным способом):

SetI2cSlaveAddress( addr );

предполагая, что параметр "force" будет считаться "ложным" как само собой разумеющимся.

Однако компилятор (в режиме С++ 11) пожаловался и дал мне следующую ошибку компилятора:

/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp: In member function 'void CMax6956Io::Init(unsigned char, unsigned char, unsigned int)':
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: error: no matching function for call to 'CMax6956Io::SetI2cSlaveAddress(unsigned char&)'
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: note: candidate is:
In file included from /home/geertvc/mystuff/domoproject/lib/i2cdevs/../../include/i2cdevs/max6956io.h:35:0,
                 from /home/geertvc/mystuff/domoproject/lib/i2cdevs/max6956io.cpp:1:
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: int CI2cHal::SetI2cSlaveAddress(unsigned char, bool)
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note:   candidate expects 2 arguments, 1 provided
make[2]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/max6956io.cpp.o] Error 1
make[1]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/all] Error 2
make: *** [all] Error 2

Но когда я добавил параметр по умолчанию в заголовок файла базового класса:

int SetI2cSlaveAddress( UCHAR addr, bool force = false );

и удалил его из исходного файла базового класса:

int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force )

тогда компилятор был счастлив, и весь код работал как ожидалось (я мог бы дать один или два параметра функции SetI2cSlaveAddress())!

Таким образом, не только для пользователя класса важно поместить значение по умолчанию параметра в файл заголовка, а также компиляцию и функциональный мудрый, по-видимому, кажется обязательным!

Ответ 5

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

Если функции являются частными и внешними, то имеет смысл помещать значения по умолчанию в файл реализации, поскольку это позволяет изменять, не вызывая повторной компиляции клиента (иногда это серьезная проблема для низкоуровневых библиотек в развитии масштаба предприятия). Тем не менее, это определенно потенциально сбивает с толку, и есть ценность документации при представлении API более интуитивно понятным способом в заголовке, поэтому выберите свой компромисс - хотя согласованность - главное, когда нет никаких веских причин в любом случае.

Ответ 6

объявление обычно является наиболее "полезным", но это зависит от того, как вы хотите использовать класс.

оба недействительны.

Ответ 7

Хороший вопрос... Я считаю, что кодеры обычно используют объявление для объявления значений по умолчанию. Меня держали в одном направлении (или предупреждали) или в другом, основанном на компиляторе

void testFunct(int nVal1, int nVal2=500);
void testFunct(int nVal1, int nVal2)
{
    using namespace std;
    cout << nVal1 << << nVal2 << endl;
}

Ответ 8

Еще один момент, о котором я не упоминал:

Если у вас есть виртуальный метод, каждое объявление может иметь собственное значение по умолчанию!

Это зависит от интерфейса, который вы вызываете, какое значение будет использоваться.

Пример ideone

struct iface
{
    virtual void test(int a = 0) { std::cout << a; }
};

struct impl : public iface
{
    virtual void test(int a = 5) override { std::cout << a; }
};

int main()
{
    impl d;
    d.test();
    iface* a = &d;
    a->test();
}

Он печатает 50

Я категорически не рекомендую использовать его так

Ответ 9

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

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

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

Ответ 10

Добавление еще одной точки. Объявление функций с аргументом по умолчанию должно быть упорядочено справа налево и сверху вниз.

Например, в объявлении функции ниже, если вы измените порядок объявления, тогда компилятор выдаст вам ошибку по умолчанию по умолчанию. Причина, по которой компилятор позволяет отделить объявление функции с аргументом по умолчанию в пределах той же области, но он должен быть в порядке от ВРАЩАЮТ к LEFT (аргументы по умолчанию) и от TOP до BOTTOM (порядок описания аргумента функции объявления).

//declaration
void function(char const *msg, bool three, bool two, bool one = false);
void function(char const *msg, bool three = true, bool two, bool one); // Error 
void function(char const *msg, bool three, bool two = true, bool one); // OK
//void function(char const *msg, bool three = true, bool two, bool one); // OK

int main() {
    function("Using only one Default Argument", false, true);
    function("Using Two Default Arguments", false);
    function("Using Three Default Arguments");
    return 0;
}

//definition
void function(char const *msg, bool three, bool two, bool one ) {
    std::cout<<msg<<" "<<three<<" "<<two<<" "<<one<<std::endl;
}