我的代码库中有各种函数,它们接受一个通用的可调用对象,并在调用它之前将其传递给一系列嵌套的 lambda。例子:
template <typename TF>
void interface(TF&& f)
{
nested0([/*...*/]()
{
nested1([/*...*/](auto& x)
{
nested2([&x, /*...*/]()
{
f(x);
});
});
});
}
请注意,这是通过转发引用(以前称为通用引用)interface
获取类型的可调用对象。可调用对象通常是一个 lambda,其中包含通过值和引用捕获的各种变量。TF
f
在保持正确性的同时在嵌套 lambda中捕获的最佳(就性能而言)方法是什么?
我可以想到三个选项:
f
通过复制捕获。nested0([f]() { nested1([f](auto& x) { nested2([&x, f]() { f(x); }); }); });
可能会导致不必要的复制,如果捕获的对象是
mutable
它可能会导致不正确的行为。f
通过引用捕获。nested0([&f]() { nested1([&f](auto& x) { nested2([&x, &f]() { f(x); }); }); });
看起来很合理,但如果任何嵌套的 lambda 执行的操作比
f
. 想象一下,如果nested2
' 的主体在单独的线程中执行 -f
可能已经超出范围。通过完美转发制作 lambda
mutable
和捕获。#define FWD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__) nested0([f = FWD(f)]() mutable { nested1([f = FWD(f)](auto& x) mutable { nested2([&x, f = FWD(f)]() mutable { f(x); }); }); });
lambda 必须是,
mutable
因为我们可能会f
从 lambda 转移到另一个。这种方法似乎可以避免不必要的副本,并在可调用对象需要比原始调用者寿命更长的情况下正确移动它。
选项 3 总是最好的,还是有任何潜在的缺点?...或者也许根本没有“最佳和正确”的方式(需要有关可调用对象的知识)?