168

假设我有一个函数,它需要一个std::function

void callFunction(std::function<void()> x)
{
    x();
}

我应该x通过 const-reference 来代替吗?:

void callFunction(const std::function<void()>& x)
{
    x();
}

这个问题的答案是否会根据函数对它的作用而改变?例如,如果它是一个类成员函数或构造函数,它将存储或初始化std::function为成员变量。

4

3 回答 3

88

如果您想要性能,请在存储时按值传递。

假设您有一个名为“在 UI 线程中运行”的函数。

std::future<void> run_in_ui_thread( std::function<void()> )

它在“ui”线程中运行一些代码,然后发出future完成时的信号。(在 UI 线程应该与 UI 元素混在一起的 UI 框架中很有用)

我们正在考虑两个签名:

std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)

现在,我们可能会按如下方式使用它们:

run_in_ui_thread( [=]{
  // code goes here
} ).wait();

这将创建一个匿名闭包(一个 lambda),从中构造一个std::function,将其传递给run_in_ui_thread函数,然后等待它在主线程中完成运行。

在情况 (A) 中,std::function直接从我们的 lambda 构造,然后在run_in_ui_thread. lambda 是moved 到 中的std::function,因此任何可移动状态都可以有效地带入其中。

在第二种情况下,std::function创建了一个临时对象,将 lambdamove放入其中,然后std::functionrun_in_ui_thread.

到目前为止,一切都很好——他们两个的表现是一样的。除了将run_in_ui_thread复制其函数参数以发送到 ui 线程执行!(它会在完成之前返回,所以它不能只使用对它的引用)。对于情况(A),我们简单movestd::function将其放入长期存储。在情况 (B) 中,我们被迫复制std::function.

那家商店使价值传递更加优化。如果您有任何可能存储 的副本,请std::function按值传递。否则,任何一种方式都大致等效:按值的唯一缺点是,如果您采用相同的体积std::function并且有一个又一个子方法使用它。除此之外, amove将与 a 一样有效const&

现在,如果我们在std::function.

假设std::function存储了一些带有 a 的对象operator() const,但它也有一些mutable它修改的数据成员(多么粗鲁!)。

在这种std::function<> const&情况下,mutable修改的数据成员将传播到函数调用之外。在这种std::function<>情况下,他们不会。

这是一个比较奇怪的极端案例。

你想像对待std::function任何其他可能很重、便宜的可移动字体一样对待。搬家很便宜,复制可能很贵。

于 2013-08-21T19:42:37.797 回答
36

如果您担心性能,并且您没有定义虚拟成员函数,那么您很可能根本不应该使用std::function

使函子类型成为模板参数允许比 更大的优化std::function,包括内联函子逻辑。这些优化的效果可能会大大超过复制与间接有关如何传递的担忧std::function

快点:

template<typename Functor>
void callFunction(Functor&& x)
{
    x();
}
于 2013-08-21T19:11:31.027 回答
30

与 C++11 中的往常一样,按值/引用/常量引用传递取决于您对参数的处理方式。std::function没有什么不同。

按值传递允许您将参数移动到变量中(通常是类的成员变量):

struct Foo {
    Foo(Object o) : m_o(std::move(o)) {}

    Object m_o;
};

当您知道您的函数将移动其参数时,这是最好的解决方案,这样您的用户可以控制他们如何调用您的函数:

Foo f1{Object()};               // move the temporary, followed by a move in the constructor
Foo f2{some_object};            // copy the object, followed by a move in the constructor
Foo f3{std::move(some_object)}; // move the object, followed by a move in the constructor

我相信您已经知道(非)const 引用的语义,所以我不会强调这一点。如果您需要我对此添加更多解释,请询问,我会更新。

于 2013-08-21T19:16:48.527 回答