繁体   English   中英

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

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

假设我有一个带std::function

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

我应该通过const-reference传递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 lambda movestd::function ,因此任何可移动状态都有效地进入它。

在第二种情况下,临时std::function被创建,拉姆达是move d到它,则该临时std::function使用由所述内参考run_in_ui_thread

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

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

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

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

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

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

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

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

使functor类型成为模板参数允许比std::function更好的优化,包括内联functor逻辑。 这些优化的效果可能大大超过了关于如何传递std::function的copy-vs-indirection问题。

快点:

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

像往常一样在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的语义,所以我不会强调这一点。 如果您需要我添加更多关于此的解释,请询问并且我将更新。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM