如何将函数指针和函数对象包装在通用代码中?

时间:2013-07-19 23:47:59

标签: c++ templates c++11 function-pointers functor

以下模板定义

template <typename Func, typename ReturnType, typename... Arguments>
class Command
{
public:
    Command(Func f) : m_func(f) { }
    ReturnType operator()(Arguments... funcArgs) { return m_func(funcArgs...); }
private:
    Func m_func;
};

在使用以下测试代码实例化时,使用gcc 4.7.3(错误:字段'Command :: m_func'无效声明的函数类型)给出错误消息:

void testFunction(int i, double d)
{
    std::cout << "TestFunctor::operator()(" << i << ", " << d << ") called." << std::endl;
}

int main()
{
    void (&fRef)(int, double) = TestFunction;
    Command<void(int, double), void, int, double> testCommand(fRef);
}

如果我将没有address-of运算符的TestFunction传递给testCommand构造函数,也会出现错误消息,但如果我传递一个显式命名的函数指针或使用address-of运算符传递参数,则会消失。我认为这段代码应该适用于现代C ++设计的第5章。

无法存储函数引用的原因是什么,但函数指针工作正常?是否有任何变通方法可以在不失去对作为Command的构造函数的参数传递函子的支持的情况下进行编译?

2 个答案:

答案 0 :(得分:5)

更改一行可以修复它:

Command<void(*)(int, double), void, int, double> testCommand(fRef);

不同之处在于,您现在传递的是函数指针,而不是函数类型。 (函数不可复制,但指针是。)

传递时,引用fRef 衰减到函数指针。

如果性能很重要,我建议不要使用std::function

查看 live on Coliru

请注意,只需稍加重写,您就可以更好地完成所有工作:

int main()
{
    auto command = make_command(testFunction);
    command(1, 3.14);
}

为此,我建议将Command模板更改为:

template <typename Func>
class Command
{
    Func m_func;
public:
    Command(Func f) : m_func(f) { }

    template <typename... A> auto operator()(A... args) const 
        -> decltype(m_func(args...)) 
    { return m_func(args...); }
};

现在,您可以通过拥有工厂函数对Func模板参数进行类型推导:

template <typename Func> Command<Func> make_command(Func f)
{
    return Command<Func>(f);
}

也请参阅此方法 live on Coliru 。当然,输出也一样:

TestFunctor::operator()(1, 3.14) called.

答案 1 :(得分:2)

C ++ 11提供std::function模板。你不必乱用函数指针。

您可以通过引用传递它们,复制它们,移动它们甚至可以用来存储lambdas:

std::function<void()> func = []() { std::cout << "Hi" << std::endl; };