Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why can't default argument depend on non-default argument?

Tags:

c++

c++11

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?

like image 824
Jorge Leitao Avatar asked Jun 20 '16 09:06

Jorge Leitao


3 Answers

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

like image 102
WhiZTiM Avatar answered Oct 06 '22 12:10

WhiZTiM


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

like image 35
Jorge Leitao Avatar answered Oct 06 '22 13:10

Jorge Leitao


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.

like image 38
Ajay Avatar answered Oct 06 '22 14:10

Ajay