Можно ли инициализировать аргумент C++ по умолчанию с помощью другого аргумента? [дубликат]


этот вопрос уже есть ответ здесь:

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

то есть, может ли работать следующее?

RateLimiter(unsigned double rateInPermitsPerSecond, 
            unsigned int maxAccumulatedPermits = rateInPermitsPerSecond);

В настоящее время я получаю ошибка:

RateLimiter.h: 13: ошибка ‘ ' rateInPermitsPerSecond’ не был объявлен в этой области

5 57
c++

5 ответов:

еще один аргумент не может быть использован в качестве значения по умолчанию. В стандарте говорится:

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

и иллюстрирует это следующим примером:

int f(int a, int b = a); // error: parameter a
                         // used as default argument

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

вместо этого используйте перегрузку:

void fun(int, int) {}

void fun(int i) {
    fun(i, i);
}

Я искал логическое объяснение, почему это не допускается

Это на самом деле хороший вопрос. Причина в том, что c++ не определяет порядок вычисления аргументов.

Итак, давайте представим себе немного более сложный сценарий:

int f(int a, int b = ++a);

... followed by ...

int a = 1;
f(a);

C++ не предписывает порядок оценки аргументов, помните?

так каким же должно быть значение b?

f(a) можно оценить в либо:

f(1, 2), или f(2, 2) в зависимости от порядка вычисления аргументов.

таким образом, поведение будет неопределенным (и даже неопределимой).

Далее, рассмотрим, что может произойти, когда a и b являются сложными объектами, конструкторы и операторы копирования которых имеют побочные эффекты.

порядок этих побочных эффектов будет неопределенным.

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

void RateLimiter(unsigned int rateInPermitsPerSecond, 
                 unsigned int maxAccumulatedPermits);

inline void RateLimiter(unsigned int rateInPermitsPerSecond)
{ return RateLimiter(rateInPermitsPerSecond,rateInPermitsPerSecond); }

Это показывает, что стандарт, запрещающий это, является половинчатым, как это предлагается языком ("следовательно... не будет..."). Они просто не хотели проходить через хлопоты, делая это хорошо определенным с тем же эффектом, что и то, что явное объявление перегрузки будет делать: при желании они могли бы указать, что аргументы по умолчанию вычисляются после явно предоставленных и слева направо. Это никак не повлияет на правило, что порядок вычисления выражений аргументов в вызове функции не определен (поскольку аргументы по умолчанию не соответствуют таким выражениям; они полностью разделены и даже не находятся в одной лексической области). С другой стороны, если (как они сделали) они предпочли запретить это, они могли бы просто сказать "не должны" без необходимости оправдываться из какого-то другого правила (но, возможно, с пояснительной сноской).

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

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

int getNextDefaultID()
{
   static int id = 0;
   return ++id;
}

struct Foo
{
   Foo(int data, int id = getNextDefaultID()) : data_(data), id_(id) {}
   int data_;
   int id_;
};

int main()
{
    Foo f1(10);      // Gets the next default ID.
    Foo f2(20, 999); // ID is specified.
}