我应该通过const-reference传递std :: function吗?

时间:2013-08-21 18:58:24

标签: c++ function reference

假设我的函数需要std::function

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

我应该通过const-reference传递x吗?:

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

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

3 个答案:

答案 0 :(得分:67)

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

假设您有一个名为“在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 move进入std::function,因此任何可移动状态都可以有效地传入其中。

在第二种情况下,创建一个临时std::function,lambda为move d,然后在std::function内通过引用使用临时run_in_ui_thread

到目前为止,这么好 - 他们两个表现相同。除了run_in_ui_thread将要复制其函数参数以发送到ui线程执行! (它将在完成之前返回,因此它不能仅使用对它的引用)。对于案例(A),我们只需将move std::function纳入其长期存储空间。在情况(B)中,我们被迫复制std::function

该商店使价值传递更加优化。如果您有可能存储std::function的副本,则按值传递。否则,任何一种方式都大致相同:by-value的唯一缺点是,如果你采用相同的庞大std::function并且使用一种子方法,则使用它。除此之外,moveconst&一样有效。

现在,如果我们在std::function内有持久状态,那么两者之间还存在一些其他差异。

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

std::function<> const&的情况下,修改的mutable数据成员将传播出函数调用。在std::function<>案例中,他们不会。

这是一个相对奇怪的角落案例。

你想像对待任何其他可能重量级,价格便宜的类型那样对待std::function。移动很便宜,复制可能很昂贵。

答案 1 :(得分:30)

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

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

更快:

template<typename Functor>
void callFunction(Functor&& x)
{
    x();
}

答案 2 :(得分:23)

与C ++ 11中一样,传递value / reference / const-reference取决于你对参数的处理方式。 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-references的语义,所以我不会说明这一点。如果您需要我添加更多解释,请询问,我会更新。