下面的代码被VC ++ 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;
}
为什么?
答案 0 :(得分:10)
gcc对此错误更加友好:
error: field 'A<int(int)>::bar' invalidly declared function type
最简单的解决方案是将bar
声明为函数指针:
FunctionPtr *bar;
在这种情况下,decltype(Hello)
评估为int(int)
而不是int(*)(int)
。
答案 1 :(得分:5)
变量不能包含函数类型。您将bar
声明为FunctionPtr
,其decltype(Hello)
的计算结果为int (int)
,而不是函数指针类型。
由于从C继承的一些不一致性,这令人困惑。当您将A
的构造函数定义为采用FunctionPtr
时,您可能会想到您会得到相同的错误。但是,声明为具有数组或函数类型的函数参数(不幸的是,不方便)变为指针类型。因此,即使声明foo
具有函数类型,它实际上也具有函数指针类型并且工作正常。
但是这个规则只适用于函数参数而不适用于其他变量,所以bar
实际上确实有一个函数类型,这是不合法的。
答案 2 :(得分:3)
添加其他答案,您可以利用以下事实:
decltype(Hello)
计算为int (int)
(不是函数指针类型); 以下代码:
#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;
};
是一个通用解决方案,允许函数,函数指针,仿函数和lambdas使用相同的语法:
#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);
}
(这里我稍微改变了利用C ++ 14功能的解决方案)。
确实std::function
是一种(并非总是更好)替代品。