c++:将 shared_ptr 传递给 lambda 的有效方法

c++: Efficient way to passing shared_ptr to a lambda

我几年前创建了一个小程序,最近开始使用智能指针。因此,出于实践原因,我根据 Scott Meyers - Effective Modern C++ 一书重构了部分代码。

想象一个 class,它拥有另一个 class 的成员 shared_ptr,它管理其他一些不必要的对象的生命周期。

class Foo {
...
    std::shared_ptr<Bar> manager;

    void DoSomeWork();
}

DoSomeWork 方法的实现包含一个 lambda collectIfNeighbor,因为它在某些部分经常使用,这是实现正确行为的最简单方法。主要思想是只捕获 lambda 中需要的内容,并使用智能指针而不是到处进行 nullptr 检查。

void Foo::DoSomeWork(){
    std::vector<Object*> neighbors;
    auto collectIfNeighbor = [this, &neighbors](const Position& pos) {
        ...    
        if ( *some condition* )
            neighbors.push_back(manager->GetObject(pos));
    };
    collectIfNeighbor(Position(1,1));    // example usage
    ...
}

所以这里的问题是我在 lambda 中捕获 this,但我并不需要整个,只需要管理器。将 shared_ptr 传递给 lambda 的最有效方法是什么?

如果可能我不想通过整个this因为我只使用一个成员。要么我不想创建和销毁成吨的 shared_ptr,因为这会使速度变慢。另外,不关心经理是否还活着就好了。

lambda 实现还有其他方法。将 shared_ptr 复制到本地,并通过引用传递。但是我在本地制作了额外的副本,而且每次 lambda 调用时我都很害怕。另一种方法是简单地创建一个常规指针并将其传递给 lambda。

std::shared_ptr<Bar> localManager = manager;
auto lambdaV1= [&localManager, &neighbors](const Position& pos) {
    ...    
    if ( *some condition* )
        neighbors.push_back(manager->GetObject(pos));
};
Bar* localManagerPtr = manager.get();
auto lambdaV2= [&localManagerPtr , &neighbors](const Position& pos) {
    ...    
    if ( *some condition* && manager != nullptr)    // extra check on manager
        neighbors.push_back(manager->GetObject(pos));
};

我检查了 Passing shared_ptr to lambda cause memory leak and Capture by universal ref,但对我帮助不大。

对于这种情况,我总是建议首先弄清楚这个问题:我的 lambda 在 this 的生命周期后是否可以合法调用?如果是这样,捕获 this 将不是去这里的方法。一个类似的问题是:我的 lambda 是否允许延长 this 的生命周期?如果是这样,通过 this 的共享指针捕获 this 和相应的 shared_from_this 方法是一种常见的方案,但总是有额外副本的缺点(以及可能的 unclear/complex this...的生命周期)。

在你完全清楚这些问题的前提下,这里假设lambda must/can在this的生命周期后不被调用,简单捕获this是正如 NathanOliver 所说的最简单和最便宜的解决方案,并为您提供了额外的提示,即对您的 lambda 的任何调用都需要一个合法的 this-pointer。对于只捕获一个参数,如果没有其他成员,largest_prime 的简单原始指针捕获解决方案有时会更明确或至少更清晰(不像 this-capture 这样的锤子)或成员方法在你的 lambda 主体中是必需的,即也许 lambda 的工作在语义上与你的实际 class 有点分离,并且可能在你的 class 不属于上下文的地方进一步使用通过设计。