7

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?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
a06e
  • 18,594
  • 33
  • 93
  • 169
  • 3
    Allowing this syntax would force the implementation to evaluate arguments from left to right? – Brian Bi Aug 16 '15 at 22:12
  • @Brian Good point. Why is it that the implementation isn't forced to evaluate arguments left to right? I forgot... – a06e Aug 16 '15 at 22:14
  • On many platforms the default calling convention is to push arguments onto the stack starting from the rightmost and proceeding left. (The callee then pops from left to right.) – Brian Bi Aug 16 '15 at 22:15
  • 1
    @Brian But there's no necessary link between the order of evaluation and the order in which the results are pushed ? – Quentin Aug 16 '15 at 22:20
  • @Quentin The calling code gets more complicated if arguments are evaluated and pushed in different orders. – Brian Bi Aug 16 '15 at 22:22
  • @Brian oh, because the result is still in the registers. Makes sense. – Quentin Aug 16 '15 at 22:23
  • Only tangentially related, but fwiw if you really had that many overloads in a single, complicated function like that, I would consider making it a function object which essentially holds the computed intermediate values, and make f a method of it, and use delegating constructors to simplify the initialization. – Chris Beck Aug 16 '15 at 23:35

2 Answers2

1

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.

Marson Mao
  • 2,935
  • 6
  • 30
  • 45
0

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]));
}
EvgeniyZh
  • 898
  • 9
  • 21