1

以下模板定义

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

如果我将没有地址运算符的 TestFunction 传递给 testCommand 构造函数,也会出现错误消息,但如果我传递显式命名的函数指针或使用地址运算符来传递参数,该错误消息就会消失。我的印象是,鉴于现代 C++ 设计的第 5 章,这段代码应该可以工作。

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

4

2 回答 2

5

改变一行可以解决它:

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

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

当您传递它时,引用fRef 会衰减到函数指针。

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

在 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);
}

也可以在 Coliru 上看到这种方法。当然,它的输出是一样的:

TestFunctor::operator()(1, 3.14) called.
于 2013-07-20T00:06:15.003 回答
2

C++11 提供了一个std::function模板。您不必弄乱函数指针。

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

std::function<void()> func = []() { std::cout << "Hi" << std::endl; };
于 2013-07-20T00:00:52.163 回答