(Если вы готовы использовать variadic macros , прокрутите до конца этого ответа, чтобы увидеть лучший ответ, который сделает все полностью вариативным. Но я думаю, что variadic macro являются просто расширением g ++.)
Его можно заставить работать, если вы готовы поместить имя функции в конец списка параметров.Поместив его позже, компилятор может вывести необходимые типы из более ранних параметров в doit
:
cout << doit(7, 6, sumall) << endl;
cout << doit(10, sumall) << endl;
Вот демоверсия для ideone .
Недостатком является то, что вам нужно реализовать один doit для каждого количества параметров.Я реализовал его только для одно- и двухпараметрических функций, но расширение этого не должно вызывать проблем:
int sumall(int a) { return a; }
int sumall(int a, int b) { return a+b; }
template<typename A1, typename A2, typename R>
auto doit( A1 a1, A2 a2, R (*f) (A1,A2)) -> R {
return f(a1, a2);
}
template<typename A1, typename R>
auto doit( A1 a1, R (*f) (A1)) -> R {
return f(a1);
}
Обновление: Иногда может показаться, чтовы можете избежать использования f
в качестве первого аргумента.Но это не так надежно, как в конце.Рассмотрим пример, в котором две функции принимают одинаковое количество аргументов, но разные типы параметров.Например:
int sumall(int a, int b) { return a+b; }
string sumall(string a, string b) { return a+" "+b; }
Вам необходимо иметь функцию в качестве последнего аргумента, чтобы при выводе шаблона можно было использовать тип и количество параметров в начале для определения типов аргументов.Вот демонстрация ideone: function-arg first и function-arg last .
Единственный недостаток, заключающийся в добавлении аргумента в конце, заключается в том, что мы не можемтогда используйте шаблоны variadic - пакеты variadic arg должны быть в конце.И вы должны точно подобрать типы - посмотрите, как мне пришлось использовать string("hi")
вместо простого "hi"
.
Использование variadic макросов , чтобы получить лучшее из всех миров
Реализуя doit
в качестве макроса и используя макросы с переменным числом (расширение gcc / g ++), можно получить полностью вариационное решение с первым именем функции.Демонстрация по ideone .
cout << doit(sumall, 7, 6) << endl;
cout << doit(sumall, 10) << endl;
cout << doit(sumall, string("hi"), string("world")) << endl;
Используя decltype
и пару других простых классов, мы можем использовать предоставленные аргументы для определения типов аргументов, а затем он можетиспользуйте его, чтобы выбрать правильный метод из набора перегрузки и определить тип возвращаемого значения.
template<typename ...Args>
struct OverloadResolved {
template<typename R>
static auto static_doit( R (*f) (Args...), Args ... args ) -> R {
return f(args...);
}
};
template<typename ...Args>
auto deduce(Args...) -> OverloadResolved<Args...> {
return OverloadResolved<Args...>();
}
template<typename T>
struct dummy : public T { };
#define doit(f, ...) ( dummy<decltype(deduce( __VA_ARGS__ ))> :: static_doit(f, __VA_ARGS__) )
Я почти уверен, что это безопасное использование макросов, ничего не будет оцениваться дважды (ничего внутри decltype
фактически выполняется.