c++ - C++ 最佳实践:通过 const 引用或转发引用(又名通用引用)将仅使用(未存储)的 lambda 参数传递给函数
问题描述
将 lambda 函数作为参数传递给仅使用(但不存储或转发)lambda 函数的函数的最佳(即最高效、最通用)的方法是什么?
选项 1:通过 const-reference 传递它是要走的路吗?
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所指出的,mutable
lambdas 不适用于选项 1。那么,另一个采用非常量左值引用的版本呢:
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
无论如何都会消除该成本。
(传递给 lambda,所以函数永远不需要知道它被传递了std::ref
) 。operator()
std::ref(lambda)
推荐阅读
- c# - 无法找到 ArgumentException 的根本原因
- c# - 字符串拆分函数如何在 C# 内部工作?
- java - 为什么此照明代码仅适用于单个灯并且在我添加多个灯时会中断?
- regex - Perl 正则表达式作为用户搜索输入(清理)
- javascript - 无法在某些文件夹中执行 fs.write 操作
- c# - 使用 access 数据库中的数据填充列表
- android - 无法从“使用 google plus 登录”中的 google 状态获取数据
- git - git:如何在变基后合并或变基?
- javascript - 如何按值对对象数组进行分组
- python - 如何使用 Python 从 Azure Functions 中的辅助线程重定向日志