a06e
a06e

Reputation: 20774

Why I cannot use previous argument values to define argument default values?

For example, why I cannot write this:

void f(double x, double y = x);

to declare a function f, for which the call f(x) is equivalent to f(x,x)?

In case this doesn't seem useful to you, here's a possible usage scenario. In this example, I declare f as follows:

void f(double x, double y = expensiveComputation(x));

where expensiveComputation denotes, you guessed it, a function that does a very slow computation. I want to give the user of f the possibility of passing in the value of y if he has computed it previously, so I don't have to compute it again inside f. Now, of course I can also resolve this by writing two overloads:

void f(double x, double y);
void f(double x) { f(x, expensiveComputation(x)); }

but writing overloads becomes tiresome as the number of arguments grows. For example, try to write:

void f(double x, double p = expensiveComputation(x), 
                 double q = expensiveComputation2(x, p), 
                 double r = expensiveComputation3(x, p, q),
                 double s = expensiveComputation3(x, p, q, r));

using overloads. It's just uglier. Default arguments are sexy. Is there a deeper syntactic reason why previous arguments can't be used to define argument default values?

Upvotes: 7

Views: 108

Answers (2)

Marson Mao
Marson Mao

Reputation: 3035

I don't know why default argument can't go that way, but maybe you can try to wrap all those arguments in a struct(class)?

struct ParamSet
{
    double x;
    double p;
    double q;

    ParamSet(double x)
        : x(x)
        , p(ExpensiveCompute1(x))
        , q(ExpensiveCompute2(x, p))
    {
    }
    ParamSet(double x, double p)
        : x(x)
        , p(p)
        , q(ExpensiveCompute2(x, p))
    {
    }
    ParamSet(double x, double p, double q)
        : x(x), p(p), q(q)
    {
    }

private:
    double ExpensiveCompute1(double x) {}
    double ExpensiveCompute2(double x, double p) {}
};      
void f(ParamSet ps);

Still need ctor overloads, but no more works than writing the expensiveComputation() series like you provided, and at least all things are wrapped in the struct

Also, signature of f() could be fixed, and there is only 1 version.

Upvotes: 1

EvgeniyZh
EvgeniyZh

Reputation: 905

You may use variadic templates for something similar:

template<typename... Args> void f(double x, Args... args)
{
    typedef typename common_type<double, Args...>::type common;
    std::vector<common, sizeof...(args)> arguments = {{ args... }};

    if (arguments.size < 2) arguments.push_back(expensiveComputation(arguments[0]));
    if (arguments.size < 3) arguments.push_back(expensiveComputation2(arguments[0], arguments[1]));
    if (arguments.size < 4) arguments.push_back(expensiveComputation3(arguments[0], arguments[1], arguments[2]));
    if (arguments.size < 5) arguments.push_back(expensiveComputation4(arguments[0], arguments[1], arguments[2], arguments[3]));
}

Upvotes: 0

Related Questions