我的代码库中有各种函数,它们接受一个通用的可调用对象,然后在调用之前将它传递给一系列嵌套的lambdas。例如:
template <typename TF>
void interface(TF&& f)
{
nested0([/*...*/]()
{
nested1([/*...*/](auto& x)
{
nested2([&x, /*...*/]()
{
f(x);
});
});
});
}
请注意,interface
通过转发引用(以前称为通用引用)来获取类型为TF
的可调用对象。可调用对象通常是具有各种捕获变量的lambda,包括值和引用。
在保持正确性的同时,在嵌套lambda中捕获f
的最佳方式(在性能方面)是什么?
我可以想到三个选择:
通过复制捕获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
已经超出了范围。
制作lambdas 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);
});
});
});
lambdas必须是mutable
,因为我们可能会将f
从lambda移动到另一个lambda。这种方法似乎可以避免不必要的副本,并在可调用对象需要比原始调用者更长时正确移动它。
选项3总是最好的,还是有任何潜在的缺点? ......或者可能没有“最佳和正确”的方式(关于可调用的知识)对象是必需的)?
答案 0 :(得分:1)
正如评论中所提到的,如果对这个问题给出如此糟糕的背景,很难说
也就是说,在我看来,最好的解决方案是通过引用捕获所有内容,并在需要副本时打破此逻辑,其目的是比f
的生命周期更长,例如:
nested0([&f]() {
n1([&f](auto& x) {
n2([&x,
// get a local copy of f
f{std::forward<TF>(f)}]() {
f(x);
});
});
});
无论如何,对于这样的问题没有经验法则 最佳解决方案可能与实际问题紧密相关 照常。很公平。