在嵌套的 lambda 中捕获通用的可调用对象 - 总是向前?

Capturing generic callable objects in nested lambdas - always forward?

我的代码库中有各种函数,它们采用通用可调用对象并在调用它之前将其传递给一系列嵌套的 lambda。示例:

template <typename TF>
void interface(TF&& f)
{
    nested0([/*...*/]()
        {
            nested1([/*...*/](auto& x)
                {
                    nested2([&x, /*...*/]()
                        {
                            f(x);
                        });
                });
        });
}

请注意,interface 通过转发引用 (以前称为通用引用) 获取类型 TF 的可调用对象。可调用对象通常是一个具有各种捕获变量的 lambda,包括按值和按引用。

在保持正确性的同时在嵌套 lambda 中捕获 f 的最佳(就性能而言)方法是什么?

我能想到三个方案:

  1. 复制捕获f

    nested0([f]()
    {
        nested1([f](auto& x)
            {
                nested2([&x, f]()
                    {
                        f(x);
                    });
            });
    });
    

    可能会导致不必要的复制,如果捕获的对象是 mutable,则可能会导致不正确的行为。

  2. 通过引用捕获 f

    nested0([&f]()
    {
        nested1([&f](auto& x)
            {
                nested2([&x, &f]()
                    {
                        f(x);
                    });
            });
    });
    

    看起来很合理,但如果任何嵌套的 lambda 执行的操作比 f 的所有者还长,则可能会导致问题。想象一下,如果 nested2 的主体在一个单独的线程中执行 - f 可能已经超出范围。

  3. 制作 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);
                    });
            });
    });
    

    lambda 必须是 mutable,因为我们可能会将 f 从一个 lambda 移动到另一个。这种方法似乎避免了不必要的复制,并在需要比原始调用者长寿时正确移动可调用对象。

选项 3 总是最好的吗,还是它有任何潜在的缺点? ...或者可能根本没有 "best and correct" 方法 (需要有关可调用对象的知识)?

如评论中所述,鉴于此问题的上下文如此糟糕,很难说。
也就是说,在我看来,最好的解决方案是通过引用捕获所有内容,并在您需要副本时打破此逻辑,其目的是超过 f 的生命周期,例如:

nested0([&f]() {
    n1([&f](auto& x) {
        n2([&x,
                // get a local copy of f
                f{std::forward<TF>(f)}]() {
            f(x);
        });
    });
});

反正这样的问题没有经验
最佳 解决方案可能与实际问题紧密相关。
照常。很公平。