我应该通过const引用传递std::函数吗

Should I pass an std::function by const-reference?

本文关键字:std 函数 const 引用 我应该      更新时间:2023-10-16

假设我有一个函数,它接受std::function:

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

我应该改为通过常量引用传递x吗?:

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

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

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

假设您有一个名为"在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中使用。λ是moved到std::function,所以任何可移动的状态都被有效地携带到其中

在第二种情况下,创建一个临时std::function,lambda是其中的moved,然后该临时std::functionrun_in_ui_thread内通过引用使用。

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

这家商店使价值传递更加优化。如果您可能正在存储std::function的副本,请传递值。否则,任何一种方法都大致相同:按值计算的唯一缺点是,如果你使用相同的庞大的std::function,并有一个又一个子方法使用它。除此之外,move的效率将与const&一样高。

现在,如果我们在std::function中有持久状态,这两者之间还有一些其他的差异。

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

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

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

你想像对待任何其他可能重量重、价格低廉的活字一样对待std::function。搬家很便宜,复印也很贵。

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

将函子类型设置为模板参数可以实现比std::function更大的优化,包括内联函子逻辑。这些优化的效果可能会大大超过关于如何传递std::function的复制与间接关注。

更快:

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

在C++11中,传递值/reference/const引用取决于您对参数的处理方式。CCD_ 34也不例外。

通过值传递可以将参数移动到变量(通常是类的成员变量)中:

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引用的语义了,所以我就不赘述了。如果你需要我对此补充更多解释,只要问一下,我就会更新。