Can a C++ default argument be initialized with another argument? [duplicate]

Solution 1:

Another argument cannot be used as the default value. The standard states:

8.3.6 Default arguments
...
9 A default argument is evaluated each time the function is called with no argument for the corresponding parameter. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in a default argument, even if they are not evaluated.

and illustrates it with the following sample:

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

Solution 2:

No, that cannot work because the evaluation of function arguments is not sequenced. It also does not work because the standard does not allow it, but I guess that was obvious.

Use an overload instead:

void fun(int, int) {}

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

Solution 3:

I was looking for an logical explanation for why it is not allowed

This is actually a good question. The reason is that C++ does not mandate the order of evaluation of arguments.

So let's imagine a slightly more complex scenario:

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

... followed by ...

int a = 1;
f(a);

C++ does not mandate the order of evaluation of arguments, remember?

So what should be the value of b?

f(a) can evaluate to either:

f(1, 2), or f(2, 2), depending on the order of evaluation of the arguments.

Thus the behaviour would be undefined (and even undefinable).

Further, consider what might happen when a and b were complex objects whose constructors and copy operators had side-effects.

The order of those side-effects would be undefined.

Solution 4:

You cannot do things like that because the standard does not allow it. However since default arguments effectively just define new function overloads, you can get the desired effect by explicitly defining such an overload:

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

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

This shows that the standard forbidding this is half-hearted, as suggested by the language ("Consequently... shall not..."). They just did not want to go through the hassle of making this well defined with the same effect as what the explicit overload declaration would do: if desired, they could have specified that defaulted arguments are evaluated after explicitly provided ones, and from left to right. This would not have any influence on the rule that the evaluation order of argument expressions in a function call is unspecified (because default arguments do not correspond to such expressions; they are entirely separate and not even in the same lexical scope). On the other hand if (as they did) they preferred to disallow this, they could have just said "shall not" without need to justify themselves from some other rule (but maybe with explanatory footnote).