Может ли тип функции быть параметром шаблона класса?
Код ниже отклоняется 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;
}
Почему?
Ответы
Ответ 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
является (не всегда лучшей) альтернативой.