假设我的函数需要std::function
:
void callFunction(std::function<void()> x)
{
x();
}
我应该通过const-reference传递x
吗?:
void callFunction(const std::function<void()>& x)
{
x();
}
这个问题的答案会根据函数的作用而改变吗?例如,如果它是一个类成员函数或构造函数,它将std::function
存储或初始化为成员变量。
答案 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
并且使用一种子方法,则使用它。除此之外,move
与const&
一样有效。
现在,如果我们在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的语义,所以我不会说明这一点。如果您需要我添加更多解释,请询问,我会更新。