如何在 C++ Lambda 中捕获向量中的元素?

How can I capture the element in a vector in C++ Lambda?

场景:

我在其中初始化了一个带有 Lambda 的实例,Lambda 可以通过引用捕获实例来修改实例状态。 当我将实例放入向量并调用 Lambda 时,Lambda 修改了原始初始化实例而不是向量中的实例。

用于说明的最小 C++ 程序:

#include <iostream>
#include <functional>
#include <vector>

struct BoolState {
    bool _stateChangeByInstanceReference = false;
    bool _stateChangeByContainerReference = false;
    std::function<void()> _changeStateByInstanceReference;
    std::function<void()> _changeStateByContainerReference;
};

int main()
{
    using namespace std;
    vector<BoolState> bss;
    BoolState bs;
    bs._changeStateByInstanceReference = [&bs]() -> void { bs._stateChangeByInstanceReference = true; };
    bs._changeStateByContainerReference = [&bss]() -> void { bss.at(0)._stateChangeByContainerReference = true; };
    bss.emplace_back(bs);
    bss.back()._changeStateByInstanceReference();
    bss.back()._changeStateByContainerReference();
    cout << "The original instance by instance reference: " << bs._stateChangeByInstanceReference << endl;
    cout << "The original instance by container reference? " << bs._stateChangeByContainerReference << endl;
    cout << "The container instance by instance reference? " << bss.back()._stateChangeByInstanceReference << endl;
    cout << "The container instance by container reference: " << bss.back()._stateChangeByContainerReference << endl;
    /*
        The original instance by instance reference: 1
        The original instance by container reference? 0
        The container instance by instance reference? 0
        The container instance by container reference: 1
    */
}

问题:

根据@Asteroids With Wings、@idclev 463035818 和@Caleth 的解决方案:

非常感谢大家!这对我了解这里发生了什么以及如何修复它有很大帮助。

push_back() 版本根据@Asteroids With Wings:

int main()
{
    using namespace std;
    vector<BoolState> bss;
    BoolState bs;
    bs._changeStateByContainerReference = [&bss]() -> void { bss.at(0)._stateChangeByContainerReference = true; };
    // Only copy container version Lambda
    bss.push_back(bs);
    // Modify in place with instance version Lambda
    BoolState& bsInbss = bss.at(0);
    bss.at(0)._changeStateByInstanceReference = [&bsInbss]() -> void { bsInbss._stateChangeByInstanceReference = true; };
    bss.at(0)._changeStateByInstanceReference();
    bss.at(0)._changeStateByContainerReference();
    cout << "The container instance by instance reference:(solved) " << bss.back()._stateChangeByInstanceReference << endl;
    cout << "The container instance by container reference: " << bss.back()._stateChangeByContainerReference << endl;
}

“真实”emplace_back() 版本根据@idclev 463035818

int main() {
    using namespace std;
    vector<BoolState> bss;
    bss.emplace_back(BoolState());
    BoolState& bsInbss = bss.at(0);
    bss.at(0)._changeStateByInstanceReference = [&bsInbss]() -> void { bsInbss._stateChangeByInstanceReference = true; };
    bss.at(0)._changeStateByContainerReference = [&bss]() -> void { bss.at(0)._stateChangeByContainerReference = true; };
    bss.at(0)._changeStateByInstanceReference();
    bss.at(0)._changeStateByContainerReference();
    cout << "The container instance by instance reference:(solved) " << bss.back()._stateChangeByInstanceReference << endl;
    cout << "The container instance by container reference: " << bss.back()._stateChangeByContainerReference << endl;
}

根据@Caleth的说法,我没有尝试再次修改原始实例,如果您仍然有兴趣相互参考,请自行尝试。

将元素放置到容器中确实会在适当的位置构建元素。使用您提供的参数调用构造函数。当您只需要 copy/move 将其放入容器时,它有助于避免创建不必要的实例。

但是,如果您已经有了一个实例,那么放置一个元素与推送它并没有太大区别:参数用于调用复制构造函数,您最终会在容器中得到一个副本。

术语可能有点误导,因为您永远不能先创建一个对象,然后将该对象放入容器中。考虑 c 数组的简单情况:

int x;
int a[3];

没有办法&x == &a[0],即容器内的元素可以与容器外的元素相等,但不相同。当然,您可以使用间接级别(例如指针)来模拟它。

When I put the instance in a vector

你不能那样做。您可以 将实例复制 [​​=19=] 到矢量元素中。在 C++ 中,对象 它占用的内存。

Can I modify the container instance state by the Lambda capturing the original reference and vice versa?

因为它们是独立的对象,所以它们需要一些相互引用的方式,例如 BoolState * other 成员。

这是真实的最小示例:

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v;
    
    int x = 42;
    v.emplace_back(x);
    
    std::cout << &x << '\n';
    std::cout << &v[0] << '\n';
}

// 0x7ffd008d9d14
// 0x1f7fc20

(live demo)

向量拥有它们的内容。他们通过复制你给他们的东西来做到这一点。

您在 main 中的对象 与向量中的对象 不同。修改一个不会修改另一个。

这并不意味着你不能做你想做的事,但你必须为向量中的对象命名才能捕获它:

BoolState& bsInVector = bss.at(0);
auto func = [&bsInVector]() { /* ... */ };