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_something2do_something1那么容易。

的优点do_something2是编译器在编写 的主体时do_something2,可以准确地知道lambda 的所有状态在哪里。

[x = 0](int& y)mutable{y=++x;}

如果此 lambda 是按值传递的,则编译器在本地知道谁有权访问x. 如果这个 lambda 是通过引用传递的,编译器必须理解调用上下文和它调用的所有代码,以便知道其他人是否x在自己的代码中的任何两个表达式之间进行了修改。

编译器通常可以比外部引用更好地优化值。

至于复制的成本,Lambda 的移动成本通常不高。如果它们是[&]-capture,则它们只是状态方面的一堆引用。如果它们是价值捕获,则很少有它们的状态巨大且难以移动。

在这些情况下,std::refstd::cref无论如何消除该成本。

std::refoperator()通行证,通过对拉姆达,因此函数永远不需要知道它是通过std::ref(lambda))。


以上是C++最佳实践:通过常量引用或转发引用(又名通用引用)将仅使用(未存储)的lambda参数传递给函数的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>