简体   繁体   English

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

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

Scenario:设想:

I initialized an instance with a Lambda in it which the Lambda can modify the instance state through capturing the instance by reference.我用 Lambda 初始化了一个实例,其中 Lambda 可以通过引用捕获实例来修改实例 state。 When I put the instance in a vector and invoke the Lambda, the Lambda modifies the original initialized instance not the instance in the vector.当我将实例放入向量中并调用 Lambda 时,Lambda 会修改原始初始化实例而不是向量中的实例。

Minimal C++ Program to illustrate:最小的 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
    */
}

Question:问题:

  • Since I captured the instance by reference in Lambda, how can the original initializied instance and the instance in vector are 2 different instance?由于我在 Lambda 中通过引用捕获了实例,原始初始化实例和向量中的实例怎么可能是 2 个不同的实例? (I can print that the 2 instances have 2 different address in memory, but I can't understand: why is this case? and how would this influence the result?) (我可以打印出 2 个实例在 memory 中有 2 个不同的地址,但我不明白:为什么会这样?这会如何影响结果?)
  • Can I modify the container instance state by the Lambda capturing the original reference and vice versa?我可以通过捕获原始引用的 Lambda 来修改容器实例 state,反之亦然?

Solution according to @Asteroids With Wings, @idclev 463035818 and @Caleth:根据@Asteroids With Wings、@idclev 463035818 和@Caleth 的解决方案:

Thank you all very much.非常感谢大家。 This helps me a lot to understand what's going on here and how to fix it.这有助于我了解这里发生了什么以及如何解决它。

push_back() version according to @Asteroids With Wings:根据@Asteroids With Wings 的 push_back() 版本:

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;
}

"real" emplace_back() version accroding to @idclev 463035818根据@idclev 463035818 的“真实”emplace_back() 版本

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;
}

Accroding to @Caleth, I did not try to modify the original instance again, feel free to try youself if you are still interested in referring each other method.根据@Caleth,我没有尝试再次修改原始实例,如果您仍然有兴趣互相引用方法,请自行尝试。

When I put the instance in a vector当我将实例放入向量中时

You can't do that.你不能那样做。 You can copy the instance into a vector element.您可以将实例复制到矢量元素中。 In C++ an object is the memory it occupies.在 C++ 和 object它占据的 memory。

Can I modify the container instance state by the Lambda capturing the original reference and vice versa?我可以通过捕获原始引用的 Lambda 来修改容器实例 state,反之亦然?

Because they are separate objects, they would need some way of referring to each other, such as a BoolState * other member.因为它们是独立的对象,所以它们需要某种相互引用的方式,例如BoolState * other成员。

Emplacing an element into a container does construct the element in place.将元素放置到容器中确实会在适当位置构造元素。 The constructor is called with the parameters provided by you.使用您提供的参数调用构造函数。 It helps to avoid creating an unnecessary instance when all you need is to copy/move it into the container.当您只需将其复制/移动到容器中时,它有助于避免创建不必要的实例。

However, if you do have an instance already, then emplacing an element is not much different from pushing it: The parameter is used to call the copy constructor and you end up with a copy in the container.但是,如果您确实已经有一个实例,那么放置一个元素与推送它并没有太大区别:该参数用于调用复制构造函数,您最终会在容器中获得一个副本。

Terminology is perhaps a little misleading, because you can never first create an object and then place that object in a container.术语可能有点误导,因为您永远不能先创建 object,然后将 object 放入容器中。 Consider the simple case of a c-array:考虑一个 c 数组的简单情况:

int x;
int a[3];

There is no way to have &x == &a[0] , ie an element inside the container can be equal, but not identical to an element outside.没有办法拥有&x == &a[0] ,即容器内的元素可以相等,但与外面的元素不同。 Of course you can emulate it with a level of indirection (eg pointers).当然,您可以使用一定程度的间接(例如指针)来模拟它。

Here's the real minimal example:这是真正的最小示例:

#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 ) 现场演示

Vectors own their contents.向量拥有它们的内容。 They do that by copying what you give them.他们通过复制你给他们的东西来做到这一点。

Your object in main is not the same object that's in the vector.您在main的 object 与向量中的 object 不同。 Modifying one will not modify the other.修改一个不会修改另一个。

That doesn't mean you can't do what you want, but you'll have to give the object that's in the vector a name in order to capture it:这并不意味着你不能做你想做的事,但你必须给向量中的 object 一个名字才能捕获它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM