void callFunction(std::function<void()> x)
{
x();
}
我应该通过const引用传递x吗?
void callFunction(const std::function<void()>& x)
{
x();
}
这个问题的答案是否取决于函数对它的处理方式?例如,如果它是一个成员函数或构造函数,将
std::function
存储或初始化为数据成员。void callFunction(std::function<void()> 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
内部使用。lambda被移动到std::function
中,因此任何可移动的状态都可以高效地传递到其中。
在第二种情况下,创建了一个临时的std::function
,然后将lambda移动到其中,然后通过引用在run_in_ui_thread
中使用该临时std::function
。
目前为止,两者表现相同。除了run_in_ui_thread
将复制其函数参数以发送到ui线程执行!(它将返回之前,所以不能仅使用对它的引用)。对于情况(A),我们只需将std::function
移动到其长期存储中即可。在情况(B)下,我们被迫复制std::function
。
该存储使按值传递更加优化。如果有可能存储std::function
的副本,则按值传递更优。否则,两种方式大致相同:唯一的缺点是按值传递的情况下,如果您正在使用相同的庞大std::function
并让一个子方法接着另一个使用它。除此之外,move
和const&
一样有效。
现在,如果在std::function
中存在持久状态,则两者之间还有一些其他差异。
假设std::function
存储一些带有operator() const
的对象,但它也具有一些可变数据成员,而它会对其进行修改(多么无礼啊!)。
在std::function<> const&
情况下,修改的可变数据成员将传播到函数调用之外。在std::function<>
情况下,它们不会传播。
这是一个相对奇怪的边角情况。
您要像处理任何其他可能很重的便宜移动类型一样处理std::function
。移动是便宜的,复制可能很昂贵。
const&
,我只看到复制操作的成本。 - ceztkostd::function
都是由lambda创建的。在(A)中,临时变量被省略为run_in_ui_thread
的参数。在(B)中,对该临时变量的引用被传递给run_in_ui_thread
。只要您的std::function
是从lambda作为临时变量创建的,那么该条款就成立。上一段讨论了std::function
持久存在的情况。如果我们不存储,只是从lambda创建,那么function const&
和function
具有完全相同的开销。 - Yakk - Adam Nevraumontstd::future<void> run_in_ui_thread( std::function<void()>&& )
。 - Pavel P如果你担心性能,并且你没有定义虚成员函数,那么你很可能根本不应该使用std::function
。
将函数对象类型作为模板参数允许进行比std::function
更大程度的优化,包括内联函数对象逻辑。这些优化的效果很可能大大超过如何传递std::function
时的复制和间接性考虑。
更快:
template<typename Functor>
void callFunction(Functor&& x)
{
x();
}
std::forward<Functor>(x)();
,以保留函数对象的值类别,因为它是“通用”引用。虽然这在99%的情况下不会有任何影响。 - GManNickGcallFunction(std::move(myFunctor));
- arias_JCstd::move
,如果你不再需要它,或者直接传递,如果你不想移动现有对象。引用折叠规则确保callFunction<T&>()
的参数类型为T&
,而不是T&&
。 - Ben Voigt像在C++11中一样,按值/引用/const引用传递取决于您对参数的操作。 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
我相信你已经了解(非)常量引用的语义,所以我不再赘述。如果你需要我添加更多关于这个的解释,请告诉我,我会进行更新。
sizeof(std::function)
不会超过2 * sizeof(size_t)
,这是您考虑常量引用的最小大小。 - Mats Peterssonstd::function
的包装器大小并不像复制它的复杂度那样重要。如果涉及深拷贝,它可能比sizeof
所建议的更昂贵。 - Ben Voigtstd::function
本身并不保存类或类的副本,对吧? - Mats Petersson