C++最佳实践:通过常量引用或转发引用(又名通用引用)将仅使用(未存储)的lambda参数传递给函数
将 lambda 函数作为参数传递给仅使用(但不存储或转发)lambda 函数的函数的最佳(即性能最高、用途最广)的方法是什么?
选项 1:通过常量引用传递它是要走的路吗?
template <typename F>
void just_invoke_func(const F& func) {
int i = 1;
func(i);
}
选项2:还是将其作为转发引用(通用引用)传递更好?
template <typename F>
void just_invoke_func(F&& func) {
int i = 1;
func(i);
}
后者比前者有什么优势吗?
其中一种解决方案是否有任何危害?
编辑#1:
选项 3:正如Yakk - Adam Nevraumont所指出的,mutablelambdas 不适用于选项 1。那么,另一个采用非常量 l 值引用的版本呢?
template <typename F>
void just_invoke_func(F& func) {
int i = 1;
func(i);
}
问题是:选项 2比选项 3有什么优势吗?
编辑#2:
选项 4:已提出另一种按值获取 lambda 的版本。
template <typename F>
void just_invoke_func(F func) {
int i = 1;
func(i);
}
回答
带有mutable关键字的Lambda 具有非常量operator(),并且在第一种情况下无法编译。
我认为没有理由阻止可变的 lambda 表达式。
在非常量左值引用情况下,右值 lambda 无法编译。
老实说,请考虑按值使用 lambda。如果调用者想要非本地状态,他们可以通过引用捕获,甚至传入std::ref/std::cref包装 lambdas(如果您正在执行不可尾调用优化的递归调用,右值引用可能是明智的)。
template<class F>
void do_something1( F&& f );
template<class F>
void do_something2( F f );
的调用do_something1(lambda)可以通过向一个呼叫被模仿do_something2(std::cref(lambda))(如果不可变)和do_something2(std::ref(lambda))(如果变)。
只有当 lambda 既是可变的又是右值时,这才不起作用;9/10 次这是一个错误(状态被处理的可变 lambda 是不确定的),在剩下的 1/10 次中,您可以解决它。
在另一方面,你不能模仿do_something2有do_something1那么容易。
的优点do_something2是编译器在编写 的主体时do_something2,可以准确地知道lambda 的所有状态在哪里。
[x = 0](int& y)mutable{y=++x;}
如果此 lambda 是按值传递的,则编译器在本地知道谁有权访问x. 如果这个 lambda 是通过引用传递的,编译器必须理解调用上下文和它调用的所有代码,以便知道其他人是否x在自己的代码中的任何两个表达式之间进行了修改。
编译器通常可以比外部引用更好地优化值。
至于复制的成本,Lambda 的移动成本通常不高。如果它们是[&]-capture,则它们只是状态方面的一堆引用。如果它们是价值捕获,则很少有它们的状态巨大且难以移动。
在这些情况下,std::ref或std::cref无论如何消除该成本。
(std::ref的operator()通行证,通过对拉姆达,因此函数永远不需要知道它是通过std::ref(lambda))。