函数类型可以是类模板参数吗?

时间:2012-11-05 13:34:39

标签: c++ templates c++11

下面的代码被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;
}

为什么?

3 个答案:

答案 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)

添加其他答案,您可以利用以下事实:

以下代码:

#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是一种(并非总是更好)替代品。