Почему я не могу использовать предыдущие значения аргументов для определения значений аргументов по умолчанию?

Например, почему я не могу написать это:

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

объявить функцию f, для которой вызов f(x) эквивалентен f(x,x)?

Если это не кажется вам полезным, вот возможный сценарий использования. В этом примере я объявляю f следующим образом:

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

где expensiveComputation обозначает, как вы уже догадались, функцию, которая выполняет очень медленные вычисления. Я хочу дать пользователю f возможность передать значение y, если он вычислил его ранее, поэтому мне не нужно вычислять его снова внутри f. Теперь, конечно, я также могу решить эту проблему, написав две перегрузки:

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

но написание перегрузок становится утомительным по мере роста числа аргументов. Например, попробуйте написать:

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

с использованием перегрузок. Это просто уродливее. Аргументы по умолчанию сексуальны. Есть ли более глубокая синтаксическая причина, по которой предыдущие аргументы нельзя использовать для определения значений аргументов по умолчанию?


person becko    schedule 16.08.2015    source источник
comment
Разрешение этого синтаксиса заставит реализацию оценивать аргументы слева направо?   -  person Brian Bi    schedule 17.08.2015
comment
@ Брайан Хороший вопрос. Почему реализация не вынуждена оценивать аргументы слева направо? я забыл...   -  person becko    schedule 17.08.2015
comment
На многих платформах соглашение о вызовах по умолчанию состоит в том, чтобы помещать аргументы в стек, начиная с самого правого и двигаясь влево. (Вызываемый затем выскакивает слева направо.)   -  person Brian Bi    schedule 17.08.2015
comment
@Brian Но нет необходимой связи между порядком оценки и порядком, в котором выводятся результаты?   -  person Quentin    schedule 17.08.2015
comment
@Quentin Вызывающий код усложняется, если аргументы оцениваются и отправляются в разном порядке.   -  person Brian Bi    schedule 17.08.2015
comment
@ Брайан, о, потому что результат все еще находится в регистрах. Имеет смысл.   -  person Quentin    schedule 17.08.2015
comment
Только косвенно, но между прочим, если бы у вас действительно было так много перегрузок в одной сложной функции, я бы подумал о том, чтобы сделать ее функциональным объектом, который по существу содержит вычисленные промежуточные значения, и сделать из него метод fa, и использовать делегирующие конструкторы для упростить инициализацию.   -  person Chris Beck    schedule 17.08.2015


Ответы (2)


Я не знаю, почему аргумент по умолчанию не может быть таким, но, может быть, вы можете попытаться обернуть все эти аргументы в структуру (класс)?

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

По-прежнему нужны перегрузки ctor, но работает не больше, чем написание серии expensiveComputation(), как вы предоставили, и, по крайней мере, все вещи завернуты в структуру

Также можно было исправить подпись f(), а версия всего 1.

person Marson Mao    schedule 28.08.2015

Вы можете использовать вариативные шаблоны для чего-то подобного:

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]));
}
person EvgeniyZh    schedule 28.08.2015