Как вызвать функцию с параметром по умолчанию через указатель на функцию, возвращающую другую функцию?

У меня есть функции Mult, Add, Div, Sub, Mod, которые принимают два целых числа и возвращают результат своих параметров. И функция Calc, которая принимает символ как Operator и возвращает указатель на функцию, которая возвращает целое число и принимает два целочисленных параметра, таких как Mult.

  • Такие функции, как Mult, вторым параметром является default Поэтому, когда я вызываю Calc, Calc возвращает адрес Mult или Add... в зависимости от значения параметра Calc, поэтому я могу передать только один аргумент.

Но он не работает с указателем на функцию:

int Add(int x, int y = 2) { // y is default
    return x + y;
}

int Mult(int x, int y = 2) { // y is default
    return x * y;
}

int Div(int x, int y = 2) { // y is default
    return y ? x / y : -1;
}

int Sub(int x, int y = 2) { // y is default
    return x - y;
}

int Mod(int x, int y = 2) { // y is default
    return y ? x % y : -1;
}

using pFn = int(*)(int, int);


pFn Calc(char c) {
    switch (c) {
        case '+':
            return Add;
        case '*':
            return Mult;
        case '/':
            return Div;
        case '-':
            return Sub;
        case '%':
            return Mod;
    }
    return Mult;
}

int main(int argc, char* argv[]){

    pFn func = Calc('%');
    cout << func(7, 4) << endl; // ok
    //cout << func(7) << endl; // error:  Too few arguments
    cout << Mult(4) << endl; // ok. the second argument is default

    func = Calc('/'); // ok
    cout << func(75, 12) << endl; // ok

    std::cout << std::endl;
}

Выше, если я вызываю Mult с одним аргументом, он работает нормально, потому что второй аргумент по умолчанию, но вызов его через указатель func не работает. func — это указатель на функцию, которая принимает два целых числа и возвращает целое число.


person Syfu_H    schedule 16.04.2019    source источник
comment
Какой смысл Double принимать целочисленный параметр, который он не использует?   -  person scohe001    schedule 16.04.2019
comment
Аналогично: Howto: указатель функции c++ со значениями по умолчанию   -  person TrebledJ    schedule 16.04.2019
comment
@scohe001: В реальном примере, например, в зависимости от значения параметра Double функция возвращает указатель на функцию из нескольких вариантов: например: switch(x){ case 1: return Mult; break; case 2: return Add;}.   -  person Syfu_H    schedule 17.04.2019
comment
@Syfu_H Вы пробовали очевидное - например. добавление значений по умолчанию в прототип функции.   -  person AnArrayOfFunctions    schedule 18.04.2019
comment
@AnArrayOfFunctions: я не понял твоей точки зрения. В приведенном выше примере все функции имеют второй параметр по умолчанию.   -  person Syfu_H    schedule 18.04.2019
comment
@Syfu_H Но не тот тип. Я не очень хорошо знаю C++, но using pFn = int(*)(int, int = 2); или что-то в этом роде может сработать.   -  person glglgl    schedule 18.04.2019
comment
@glglgl: Нет. Не разрешается определять указатель на функцию с аргументом по умолчанию. Потому что параметр по умолчанию не является частью типа указателя.   -  person Syfu_H    schedule 18.04.2019
comment
@Syfu_H А, хорошо. Не знал этого, спасибо.   -  person glglgl    schedule 19.04.2019
comment
@glglgl: Добро пожаловать.   -  person Syfu_H    schedule 19.04.2019


Ответы (3)


Аргументы по умолчанию — это немного синтаксического сахара C++; при прямом вызове функции с недостаточными аргументами компилятор вставляет значение по умолчанию, как если бы вызывающий объект передал его явно, поэтому функция по-прежнему вызывается с полным набором аргументов (в этом случае Mult(4) компилируется в тот же код, что и Mult(4, 2)).

Однако значение по умолчанию на самом деле не является частью типа функции, поэтому вы не можете использовать значение по умолчанию для косвенного вызова; синтаксический сахар там ломается, так как как только вы звоните через указатель, информация о значениях по умолчанию теряется.

person ShadowRanger    schedule 16.04.2019
comment
Спасибо! Я действительно ценю это. Также: я отредактировал вопрос. Вы можете отредактировать его. Теперь я создаю функцию Calc, которая решает, какую функцию вернуть, в зависимости от аргумента Character, переданного в Calc. - person Syfu_H; 17.04.2019
comment
@Syfu_H, пожалуйста, не меняйте существенно вопрос после того, как получите ответы. Это был отличный ответ на исходный вопрос, и на самом деле я думаю, что он все еще - person 463035818_is_not_a_number; 17.04.2019

На вопрос «почему бы и нет» я отсылаю вас к этому ответу. Если вы хотите каким-то образом сохранить возможность использовать значение по умолчанию, вам нужно предоставить что-то большее, чем указатель на функцию, например, подойдет лямбда:

auto Double() {
    return [](int x,int y=2){ return Mult(x,y); };
}

А с помощью вариативной лямбды (спасибо @Artyer) вам даже не нужно повторять значение по умолчанию:

#include <iostream>

int Mult(int x, int y = 2) { // y is default
    return x * y;
}

auto Double() {
    return [](auto... args) { return Mult(args...); };
}

int main(int argc, char* argv[]){    
    auto func = Double();
    std::cout << func(7, 4) << '\n'; // ok
    std::cout << func(7) << '\n';    // ok
    std::cout << Mult(4) << '\n';    // ok
}

Демо

person 463035818_is_not_a_number    schedule 16.04.2019
comment
Обратите внимание, что это включает явное повторение значения по умолчанию внутри Double при определении lambda, что значительно ограничивает утилиту. - person ShadowRanger; 16.04.2019
comment
@ShadowRanger да, добавил примечание - person 463035818_is_not_a_number; 16.04.2019
comment
Чтобы не повторять значения по умолчанию, просто перешлите вариативные аргументы: return [](auto... args) { return Mult(args...); } . Или с идеальной переадресацией (что здесь не очень нужно, потому что это просто копирует ints, но может быть для других функций) return [](auto&&... args) noexcept(noexcept(Mult(std::forward<decltype(args)>(args)...))) -> decltype(auto) { return Mult(std::forward<decltype(args)>(args)...); }; - person Artyer; 16.04.2019
comment
@Артьер, спасибо. не опубликовал переадресацию, потому что мне нужно было сначала понять это самому, а для ints это не стоило труда - person 463035818_is_not_a_number; 16.04.2019

Если у вас всегда есть 2 в качестве аргумента по умолчанию, вы можете обернуть указатель на функцию в простой вспомогательный класс, например:

using pFn_ = int(*)(int, int);

class pFn
{
    pFn_ ptr;
public:
    pFn(pFn_ p) : ptr(p) {}
    int operator()(int x, int y = 2) const {
        return ptr(x,y);
    }
};

Полный рабочий пример: https://godbolt.org/z/5r7tZ8

person chtz    schedule 17.04.2019