3

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

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

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

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

我可以想到三个选项:

  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. 通过完美转发制作 lambdamutable和捕获。

    #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 总是最好的,还是有任何潜在的缺点?...或者也许根本没有“最佳和正确”的方式(需要有关可调用对象的知识)

4

1 回答 1

1

正如评论中所提到的,很难说这个问题的背景如此糟糕。
也就是说,在我看来,最好的解决方案是通过引用捕获所有内容,并在您需要副本时打破此逻辑,其目的是延长 的生命周期f,例如:

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

无论如何,对于这样的问题没有经验法则。最佳解决方案可能与实际问题紧密相关
。 照常。很公平。

于 2016-04-23T13:41:42.643 回答