如何在 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
*/
}
问题:
- 由于我在 Lambda 中通过引用捕获了实例,如何才能
原始初始化实例和向量中的实例为 2
不同的实例? (我可以打印出这 2 个实例在内存中有 2 个不同的地址,但我不明白:为什么会这样?这将如何影响结果?)
- 我能否通过捕获原始引用的 Lambda 修改容器实例状态,反之亦然?
根据@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]() { /* ... */ };
场景:
我在其中初始化了一个带有 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
*/
}
问题:
- 由于我在 Lambda 中通过引用捕获了实例,如何才能 原始初始化实例和向量中的实例为 2 不同的实例? (我可以打印出这 2 个实例在内存中有 2 个不同的地址,但我不明白:为什么会这样?这将如何影响结果?)
- 我能否通过捕获原始引用的 Lambda 修改容器实例状态,反之亦然?
根据@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]() { /* ... */ };