C++ 我应该通过常量引用传递 std::function 吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18365532/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:56:02  来源:igfitidea点击:

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

c++functionreference

提问by Sven Adbring

Let's say I have a function which takes an std::function:

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

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

Should I pass xby const-reference instead?:

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

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

Does the answer to this question change depending on what the function does with it? For example if it is a class member function or constructor which stores or initializes the std::functioninto a member variable.

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

回答by Yakk - Adam Nevraumont

If you want performance, pass by value if you are storing it.

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

Suppose you have a function called "run this in the UI thread".

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

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

which runs some code in the "ui" thread, then signals the futurewhen done. (Useful in UI frameworks where the UI thread is where you are supposed to mess with UI elements)

它在“ui”线程中运行一些代码,然后future在完成时发出信号。(在 UI 框架中很有用,其中 UI 线程是您应该弄乱 UI 元素的地方)

We have two signatures we are considering:

我们正在考虑两个签名:

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

Now, we are likely to use these as follows:

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

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

which will create an anonymous closure (a lambda), construct a std::functionout of it, pass it to the run_in_ui_threadfunction, then wait for it to finish running in the main thread.

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

In case (A), the std::functionis directly constructed from our lambda, which is then used within the run_in_ui_thread. The lambda is moved into the std::function, so any movable state is efficiently carried into it.

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

In the second case, a temporary std::functionis created, the lambda is moved into it, then that temporary std::functionis used by reference within the run_in_ui_thread.

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

So far, so good -- the two of them perform identically. Except the run_in_ui_threadis going to make a copy of its function argument to send to the ui thread to execute! (it will return before it is done with it, so it cannot just use a reference to it). For case (A), we simply movethe std::functioninto its long-term storage. In case (B), we are forced to copy the std::function.

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

That store makes passing by value more optimal. If there is any possibility you are storing a copy of the std::function, pass by value. Otherwise, either way is roughly equivalent: the only downside to by-value is if you are taking the same bulky std::functionand having one sub method after another use it. Barring that, a movewill be as efficient as a const&.

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

Now, there are some other differences between the two that mostly kick in if we have persistent state within the std::function.

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

Assume that the std::functionstores some object with a operator() const, but it also has some mutabledata members which it modifies (how rude!).

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

In the std::function<> const&case, the mutabledata members modified will propagate out of the function call. In the std::function<>case, they won't.

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

This is a relatively strange corner case.

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

You want to treat std::functionlike you would any other possibly heavy-weight, cheaply movable type. Moving is cheap, copying can be expensive.

您想像对待std::function任何其他可能重量大、价格低廉的可移动类型一样对待。搬家很便宜,复印可能很贵。

回答by Ben Voigt

If you're worried about performance, and you aren't defining a virtual member function, then you most likely should not be using std::functionat all.

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

Making the functor type a template parameter permits greater optimization than std::function, including inlining the functor logic. The effect of these optimizations is likely to greatly outweigh the copy-vs-indirection concerns about how to pass std::function.

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

Faster:

快点:

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

回答by syam

As usual in C++11, passing by value/reference/const-reference depends on what you do with your argument. std::functionis no different.

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

Passing by valueallows you to move the argument into a variable (typically a member variable of a class):

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

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

    Object m_o;
};

When you know your function will move its argument, this is the best solution, this way your users can control how they call your function:

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

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

I believe you already know the semantics of (non)const-references so I won't belabor the point. If you need me to add more explanations about this, just ask and I'll update.

我相信你已经知道(非)const-references 的语义,所以我不会详细说明这一点。如果您需要我对此添加更多解释,请询问,我会更新。