在嵌套lambda中捕获通用可调用对象-始终向前? [英] Capturing generic callable objects in nested lambdas - always forward?

查看:95
本文介绍了在嵌套lambda中捕获通用可调用对象-始终向前?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have various functions in my codebase that take a generic callable object and pass it to a series of nested lambdas before calling it. Example:

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

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

Note that interface is taking a callable object of type TF by forwarding reference (previously known as universal reference). The callable object is usually a lambda with various captured variables, both by value and by reference.

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

What is the best (in terms of performance) way of capturing f in the nested lambdas while maintaining correctness?

我可以想到三个选择:


  1. 通过拷贝捕获 f

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

可能导致不必要的复制,并且如果捕获的对象是可变的可能会导致错误的行为。

Could cause unnecessary copies, and if the captured object is mutable it could cause incorrect behavior.

通过引用捕获 f

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

看似合理,但如果嵌套的lambda中的任何一个执行的动作超出了其所有者的寿命,则可能会引起问题 f 。想象一下,如果 nested2 的主体是在单独的线程中执行的- f 可能已经超出范围。

Seems reasonable, but could cause problems if any of the nested lambdas perform an action that outlives the owner of f. Imagine if nested2's body was executed in a separate thread - f could already be out of scope.

使lambdas 可变,并通过完美转发来捕获。

Make the lambdas mutable and capture by perfect-forwarding.

#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移到了另一个。这种方法似乎避免了不必要的复制,并在需要超出原始调用者寿命的情况下正确移动了可调用对象。

The lambdas have to be mutable because we're potentially moving f from a lambda to another one. This approach seems to avoid unnecessary copies and to correctly move the callable object if it needs to outlive the original caller.

选项3始终是最好的,还是有任何潜在的缺点? ...或者也许根本没有最佳和正确的方法(需要了解可调用对象)

Is option 3 always the best one, or does it have any potential drawback? ...or maybe there is no "best and correct" way at all (knowledge about the callable object is required)?

推荐答案

正如评论中提到的,很难说出问题的背景如此糟糕。

也就是说,我认为最好的解决方案是通过引用捕获所有内容,并在需要复制时打破此逻辑,其目的是使 f的寿命更长,例如:

As mentioned in the comments, it's difficult to say with such a poor context given about the problem.
That said, the best solution seems to me to capture everything by reference and break this logic whenever you need a copy the aim of which is to outlive the lifetime of f, as an example:

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

无论如何,对于此类问题没有经验法则。

最佳解决方案与实际问题紧密相关。

和往常一样。足够公平。

Anyway, there is no rule of thumb for such a problem.
The best solution is tightly bound likely to the actual problem.
As usual. Fair enough.

这篇关于在嵌套lambda中捕获通用可调用对象-始终向前?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆