Использование типа функции в std:: function для объявления нескольких функций этого типа

Предположим, что

typedef std::function<
    double(
        int,
        long
    )
> FooType;

и я хочу объявить прототипы функций для ряда функций, которые я могу сложить в std::function этого типа. Я знаю, что могу написать

double foo1(int, long);
double foo2(int, long);

и т.д., но есть ли способ, каким образом я могу использовать FooType при объявлении прототипов функций? Что-то вроде

FooType::type foo1, foo2;

Возможно, мне, возможно, придется использовать (*foo1) или подобное? Естественно, в реализации функции мне нужно было бы написать ее длинную руку, чтобы я мог вставить некоторые параметры, но запись ее, как указано выше, сохранит мой файл заголовка чистым.

Ответы

Ответ 1

Конечно, вы можете, как всегда *, использовать частичную специализацию:

template <typename> struct fn_sig;
template <typename T> struct fn_sig<std::function<T>> { using type = T; };

Использование:

fn_sig<FooType>::type f;

double f(int a, long b) { return double(a) /  b; }

(Очевидно, для определения функции вам, вероятно, потребуется указать базовый тип функции.)



*) Значение того, что это тот же ответ на любой вопрос о форме "могу ли я получить параметр шаблона из специализированного шаблона".

Ответ 2

запись его, как указано выше, сохранит мой файл заголовка

Было бы проще (с меньшим метапрограммированием) пойти наоборот: Начните с объявления foo() и создайте FooType из него.

double foo(int, long);

typedef std::function< decltype(foo) > FooType;

Так как существует предположительно несколько таких функций, оптимальным факторингом может быть два typedefs:

typedef double FooFn(int, long);
typedef std::function< FooFn > FooType;

FooFn foo1, foo2, foo3;

Конечно, в файле реализации (.cpp) подпись должна быть написана долго.

Еще одна вещь, обратите внимание, что std::function представляет собой тяжелое обобщение указателей на функции. Если вы только присвоите ему обычные функции, указатель функции может быть лучше, и это будет замена:

typedef FooFn * FooType;

Ответ 3

Вы можете легко сделать это, создав шаблон struct, который разрушает тип FooType, сопоставляя T в std::function< T >:

template <typename T>
struct destructure;

template <typename T>
struct destructure<std::function<T>>
{
    using type = T;
};

template <typename T>
using destructure_t = typename destructure<T>::type;

После этого вы можете использовать destructure_t для объявления своей функции:

destructure_t<FooType> foo;

int main()
{
    foo(1, 20l);
}

Затем вы можете определить его с помощью регулярного синтаксиса функции:

double foo(int i, long l)
{
    std::cout << i << " " << l << "\n";
    return 0;
}

Ваш main будет печатать "1 20".

пример wandbox

Ответ 4

В качестве альтернативного решения для рабочего, представленного @KerrekSB, вы можете использовать объявление функции и псевдоним, как следует, если вы хотите получить тип указателя

#include <functional>
#include <utility>

template<typename F> F * get(std::function<F>);
template<typename F> using FPtr = decltype(get(std::declval<F>()));

double f(int, long) { return {}; }

int main() {
    using FooType = std::function<double(int, long)>;
    FPtr<FooType> myF = f;
    (void)myF;
}

Или немного модифицированная версия, если вы хотите только фактический тип:

#include <type_traits>
#include <functional>
#include <utility>

template<typename F> F * get(std::function<F>);
template<typename F> using MyType = typename std::remove_pointer<decltype(get(std::declval<F>()))>::type;

double f(int, long) { return {}; }

int main() {
    using FooType = std::function<double(int, long)>;
    MyType<FooType> *myF = f;
    (void)myF;
}