Перегруженная функция в качестве аргумента функции шаблона - PullRequest
7 голосов
/ 29 января 2012

Я пытаюсь сделать шаблонную функцию переменной, которая принимает в качестве аргументов перегруженную функцию и ее аргументы:)

int sumall(int a) { return a; }
int sumall(int a, int b) { return a+b; }

template<typename R, typename... A>
R doit( R(*f)(A...), A... a) {
    return f(a...); }

Я хочу позвонить doit без каких-либо спецификаторов шаблонов или приведения:

cout << doit(sumall, 7, 6) << endl

Это не компилируется, но когда возвращаемые типы недействительны, все работает идеально:

void printsum(int a) { cout << a << endl; }
void printsum(int a, int b) { cout << a+b << endl; }

template<typename... A>
void vdoit( void(*f)(A...), A... a) {
    f(a...); }

// ...
vdoit(printsum, 7, 6);

Можно ли изменить первый шаблон так, чтобы он работал только с шаблоном doit (я хочу сохранить функции sumall и вызов doit)? Я думаю, что это можно сделать, удалив typename R и оставив просто template<typename... A>, поскольку R зависит от A... и f, но я понятия не имею, как показать эту зависимость.

Ответы [ 2 ]

7 голосов
/ 29 января 2012

При получении указателя на функцию компилятор должен знать, какую из перегрузок вы хотите использовать.Невозможно передать указатель на «набор перегрузки», и компилятор примет решение позже.Ни один из ваших примеров не работает ни с одним из опробованных мной компиляторов (самые последние версии EDG, gcc и clang).

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

struct sumall_t {
    template <typename... T>
    auto operator()(T... args) -> decltype(sumall(args...)) {
        return sumall(args...);
    }
};

. Это эффективно создает оболочку для набора перегрузки.Поскольку тип результата не может быть выведен напрямую и может зависеть от того, как вызывается функция, вам также понадобится использовать другую версию doit():

template<typename Func, typename... A>
auto doit( Func f, A... a) ->decltype(f(a...)) {
    return f(a...);
}

Затем будет использованочто-то вроде этого:

doit(sumall_t(), 1, 2);

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

2 голосов
/ 30 января 2012

(Если вы готовы использовать 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 фактически выполняется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...