Как вызвать функцию с параметром по умолчанию через указатель на функцию, которая является возвращением другой функции?
У меня есть функции 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 - указатель на функцию, которая принимает два целых числа и возвращает int.
Ответы
Ответ 1
Значения по умолчанию - это немного синтаксического сахара C++; при непосредственном вызове функции с недостаточными аргументами компилятор вставляет значение по умолчанию, как если бы вызывающая сторона передала его явно, поэтому функция по-прежнему вызывается с полным набором аргументов (Mult(4)
компилируется в тот же код, что и Mult(4, 2)
в данном случае).
Хотя значение по умолчанию на самом деле не является частью типа функции, поэтому вы не можете использовать значение по умолчанию для косвенного вызова; синтаксический сахар разрушается, поскольку, как только вы вызываете указатель, информация о значениях по умолчанию теряется.
Ответ 2
На вопрос "почему нет" я отсылаю вас к этому ответу. Если вы хотите сохранить возможность использовать значение по умолчанию, вам нужно предоставить что-то большее, чем указатель на функцию, например, lamdba сделает:
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
}
Live демо
Ответ 3
Если у вас всегда есть 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