17

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

为什么?

4

3 回答 3

10

gcc 对这个错误更友好一点:

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

最简单的解决方案是声明bar为函数指针:

FunctionPtr *bar;

在这种情况下,decltype(Hello)计算结果为int(int)not int(*)(int)

于 2012-11-05T14:00:28.790 回答
6

变量不能有函数类型。您声明barFunctionPtrwhich is which is which is which is which is which is which isdecltype(Hello)评估为int (int),而不是函数指针类型。

由于从 C 继承的一些不一致,这很令人困惑。当您将构造函数定义A为采用 aFunctionPtr时,您可能会想象您会得到相同的错误。但是,声明为具有数组或函数类型的函数参数会自动(不幸的是,不方便)变成指针类型。因此,即使foo被声明为具有函数类型,它实际上也具有函数指针类型并且可以正常工作。

但是这条规则只适用于函数参数,不适用于其他变量,所以bar实际上确实有一个函数类型,这是不合法的。

于 2012-11-05T16:12:57.130 回答
4

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

以下代码:

#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;
};

是一个通用解决方案,允许函数、函数指针、仿函数和 lambda 具有相同的语法:

#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 特性稍微改变了解决方案)。

Indeedstd::function是一个(并不总是更好的)替代方案。

于 2017-12-06T15:39:46.133 回答