【问题标题】:Share variable between two lambdas在两个 lambda 之间共享变量
【发布时间】:2015-06-13 21:43:17
【问题描述】:

我希望能够在两个 lambda 函数之间共享包含范围内的变量。我有以下内容:

void holdAdd(const Rect& rectangle, Hold anonymousHeld, Hold anonymousFinish) {
    std::map<int,bool> identifierCollection;

    HoldFinish holdFinish = [=](const int& identifier) mutable {
        if (identifierCollection.count(identifier) == 0) return;

        identifierCollection.erase(identifier);
        anonymousFinish();
    };

    holdCollisionCollection.push_back([=](const int& identifier, const Vec2& point) mutable {
        if (rectangle.containsPoint(point)) {
            identifierCollection[identifier] = true;
            anonymousHeld();
        } else {
            holdFinish(identifier);
        }
    });
    holdFinishCollection.push_back(holdFinish);
}

我可以在调试器中看到 holdFinish 指向的 identifierCollection 的实现与第二个 lambda 函数不同。

如果我使用[=, &amp;identifierCollection],无论我是否使用mutable,它都会抛出EXC_BAD_ACCESS

我对实现内联函数的其他语言的经验是,这应该是可能的。例如在 javascript 中:

var a = 10;
var b = function() {
    a += 2;
}
var c = function() {
    a += 3;
}
b();
c();
alert(a);

会提醒15

我必须做什么才能让两个 lambda 函数引用相同的 identifierCollection 实现?这样它的行为方式与 javascript 示例相同。

【问题讨论】:

  • 所以问题是,当您按值捕获时,您已经按值捕获,而当您按引用捕获时,您已经捕获了对局部变量的悬空引用......你会有来决定你希望对象的生命周期是什么。
  • [&amp;] 有效吗?尝试制作地图static
  • 将地图包裹在shared_ptr中。
  • 你需要自己管理C++对象的生命周期;或者如果您使用shared_ptr,它将自动完成。
  • @richardjsimkins:没错。 C++ 和 JavaScript 几乎没有什么相似之处。

标签: c++ lambda anonymous-function capture


【解决方案1】:

与某些脚本语言不同,identifierCollection 的生命周期不会仅仅因为您将其捕获到闭包中而延长。因此,一旦您将 [=] 更改为 [&amp;] 以通过引用捕获,它就是对您正在捕获的局部变量的悬空引用。

您必须自己管理identifierCollection 的生命周期;坦率地说,这听起来像是共享指针的绝佳机会,通过值捕获到每个 lambda 中。只要你需要它,它所包装的动态分配的映射就会一直存在。

void holdAdd(const Rect& rectangle, Hold anonymousHeld, Hold anonymousFinish)
{
    auto identifierCollection = std::make_shared<std::map<int,bool>>();

    HoldFinish holdFinish = [=](const int& identifier) mutable {
        if (identifierCollection->count(identifier) == 0) return;

        identifierCollection->erase(identifier);
        anonymousFinish();
    };

    holdCollisionCollection.push_back([=](const int& identifier, const Vec2& point) mutable {
        if (rectangle.containsPoint(point)) {
            (*identifierCollection)[identifier] = true;
            anonymousHeld();
        } else {
            holdFinish(identifier);
        }
    });
    holdFinishCollection.push_back(holdFinish);
}

【讨论】:

  • 现在按预期工作。杰出的!谢谢:)
  • 尽管 JavaScript 不是一种脚本语言,尽管它的名字 ;) 我会说这更像是 C++ 没有垃圾收集的症状。所以我认为如果答案说“不像有垃圾收集的语言”会更好
【解决方案2】:

如果您将地图包装在 std::shared_ptr 中,则生命周期将被自动管理。然后,您的 lambda 可以按值捕获,它会获得对 map 的引用,该 map 的生命周期在 lambda 函数返回之前一直有效。

为此,请将您的地图定义更改为:

auto identifierCollection = std::make_shared<std::map<int,bool>>();

然后对映射的成员函数的任何调用都需要从使用. 更改为-&gt;(因为它现在是一个指针)。

【讨论】: