就常量性和引用性而言,函数应该如何接受 *lambda* 参数?

How should a function accept a *lambda* parameter, in terms of constness and referenceness?

以下各项的行为有何不同?

我不清楚 const 对函数参数有什么影响,以及在这种情况下按值传递、按引用传递或按右值引用传递之间的区别。

请注意,我了解按值传递和按引用传递之间的区别。但是,在 std::function 的特定情况下,或者更具体地说是 lambda,我不确定按值传递 lambda 与按引用传递 lambda 有什么区别。按值传递 lambda 意味着什么?将复制的数据是什么?

const 与 lambda 之间是否存在任何实际区别?

#include <functional>

void foo_with_func(      std::function<void()>   f) { ...; f(); ...; }
void foo_with_func(      std::function<void()>&  f) { ...; f(); ...; }
void foo_with_func(      std::function<void()>&& f) { ...; f(); ...; }
void foo_with_func(const std::function<void()>   f) { ...; f(); ...; }
void foo_with_func(const std::function<void()>&  f) { ...; f(); ...; }
void foo_with_func(const std::function<void()>&& f) { ...; f(); ...; }

所有用法相同:

foo_with_func([&]() { ... });

经验法则:

常量参数

普通旧数据类型,例如 double, int,char 应该按值传递(复制)。它们适合处理器寄存器,任何其他机制都可能需要更多的处理或内存。

较大的数据结构应该通过常量引用传递。将较大的结构复制到堆栈上会占用大量内存,并且需要额外的处理。传递引用意味着您引用的是现有项目。传递常量引用意味着该函数不会更改引用的项目。

可变参数

一个将被函数修改的参数应该通过引用传递。这允许您的程序修改项目 "in place"。

注意:我没有玩过移动语义,所以我不能推荐是否使用移动语义。

鉴于建议的用法 foo_with_func([&]() { ... });

void foo_with_func(      std::function<void()>&  f) { ...; f(); ...; }

将无法编译,因为它将非常量左值引用绑定到临时对象。其余的在任何体面的优化编译器中都是等效的。如果你还想用 std::function<void()> func; 调用它,那么通过传递 const 左值引用可能比按值传递更有效。

然而,其中

None 是最有效的,因为它们都会产生类型擦除成本。为了避免这些成本,编写一个模板并直接接受 lambda。

template<class F>
void foo_with_func(F f){ f(); }

如果 lambda 按值捕获并且复制成本很高,您也可以使用 F&&