Consider the following constructor:
class MyClass {
MyClass(unsigned int dimension, std::vector vector=unitaryVector(dimension));
};
where unitaryVector(d)
is a function that returns a random std::vector
in d dimensions.
This gives the following compiler error:
error: default argument references parameter 'dimension'
MyClass(unsigned int dimension, std::vector vector=unitaryVector(dimension));
Why isn't this idiom valid in C++11? It seems to be pretty obvious: if vector
argument is provided, init vector
as a copy of the argument, otherwise, call the function and init it as a copy of the return value. Why can't a compiler understand this?
The C++ standard forbids it.
dcl.fct.default
9 default argument is evaluated each time the function is called with no argument for the corresponding parameter. A parameter shall not appear as a potentially-evaluated expression in a default argument. Parameters of a function declared before a default argument are in scope and can hide namespace and class member names.
[ Example:
int a; int f(int a, int b = a); // error: parameter a // used as default argument typedef int I; int g(float I, int b = I(2)); // error: parameter I found int h(int a, int b = sizeof(a)); // OK, unevaluated operand
— end example ]
Note that default arguments are replaced at the call site if not provided
Intro.execution (emphasis mine)
11: [ Note: The evaluation of a full-expression can include the evaluation of subexpressions that are not lexically part of the full-expression. For example, subexpressions involved in evaluating default arguments ([dcl.fct.default]) are considered to be created in the expression that calls the function, not the expression that defines the default argument. — end note ]
You can simply overload the constructor and delegate it:
class MyClass {
explicit MyClass(unsigned int dimension)
: MyClass(dimension, unitaryVector(dimension)) //delegation
{ }
MyClass(unsigned int dimension, std::vector vector);
};
Footnote: Its a good thing to make single argument constructors explicit
One alternative is to use
class MyClass {
MyClass(unsigned int dimension, std::vector const& vector) :
dimension(dimension), vector(vector) {}
MyClass(unsigned int dimension) :
MyClass(dimension, unitaryVector(dimension)) {}
};
(this of course when you want to store dimension
and vector
in the class).
Because the default argument must be complete in itself so that compiler can simply replace it if not provided by call. The (local) variable dimension
isn't created yet and you are trying to use it, and hence the error. This would work, however:
int _def_dim=10;
class MyClass {
MyClass(unsigned int dimension, std::vector vector=unitaryVector(_def_dim));
};
I am not sure what standard says, but for compiler implementation is would be tricky to handle such corner cases.
EDIT (for completeness), grabbed from this answer:
Default arguments are evaluated each time the function is called. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in default argument expressions, even if they are not evaluated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With