Может ли тип функции быть параметром шаблона класса? - программирование

Может ли тип функции быть параметром шаблона класса?

Код ниже отклоняется VС++ 2012 с "ошибкой C2207:" A:: bar ": член шаблона класса не может получить тип функции".

int Hello(int n)
{
    return n;
}

template<class FunctionPtr>
struct A
{
    A(FunctionPtr foo)
        : bar(foo)
    {}

    FunctionPtr bar;
};

int main()
{
    A<decltype(Hello)> a(Hello);

    return 0;
}

Почему?

4b9b3361

Ответ 1

gcc немного более дружелюбен относительно этой ошибки:

error: field 'A<int(int)>::bar' invalidly declared function type

Простейшим решением является объявление bar в качестве указателя функции:

FunctionPtr *bar;

В этом случае decltype(Hello) оценивается как int(int) не int(*)(int).

Ответ 2

Переменные не могут иметь типы функций. Вы объявляете bar равным FunctionPtr, который равен decltype(Hello), который вычисляет int (int), а не тип указателя функции.

Это сбивает с толку из-за некоторых несоответствий, унаследованных от C. Когда вы определяете конструктор для A как принимающий FunctionPtr, вы можете себе представить, что получите ту же ошибку. Однако параметры функции, объявленные как имеющие тип массива или типа автоматически (к сожалению, неудобно), превращаются в типы указателей. Так что даже если foo объявлен как тип функции, он фактически имеет тип указателя функции и работает нормально.

Но это правило применяется только к параметрам функции, а не к другим переменным, поэтому bar действительно имеет тип функции, что не является законным.

Ответ 3

Добавляя к другим ответам, вы можете воспользоваться тем, что:

Следующий код:

#include <type_traits>

template<class F>
struct A
{
    A(F foo) : bar(foo) {}

    typename std::conditional<std::is_function<F>::value,
                              typename std::add_pointer<F>::type,
                              F>::type bar;
};

является универсальным решением, допускающим одинаковый синтаксис для функций, указателей функций, функторов и лямбда-выражений:

#include <type_traits>
#include <iostream>

void Hello() { std::cout << "Function\n"; }

struct Hello2 { void operator()() { std::cout << "Struct\n"; } };

void Hello3() { std::cout << "Function pointer\n"; }

template<class F>
struct A
{
  A(F foo) : bar(foo) { bar(); }

  std::conditional_t<std::is_function<F>::value, std::add_pointer_t<F>, F> bar;
};

int main()
{
  A<decltype(Hello)> a(Hello);

  Hello2 h2;
  A<decltype(h2)> b(h2);

  A<decltype(&Hello3)> c(&Hello3);

  auto Hello4 = []() { std::cout << "Lambda\n"; };
  A<decltype(Hello4)> d(Hello4);
}

(здесь я немного изменил решение, используя возможности С++ 14).

Действительно, std::function является (не всегда лучшей) альтернативой.