选择和修改嵌套向量中的条目的最佳实践
Posted
技术标签:
【中文标题】选择和修改嵌套向量中的条目的最佳实践【英文标题】:best practice to select and modify entries in nested vectors 【发布时间】:2020-02-12 13:55:02 【问题描述】:我正在寻找一种有效的方法来解决以下(可能很容易)问题:
我有一个A
类型的向量。
A
类包含B
类型的向量。
我无法更改课程A
或设计
A
类的向量(我无法将其设为指向 A
的指针向量
例如)。
我想要一个 B
类的向量,其中包含 B
的所有条目,这些条目包含在具有特殊属性的 A
向量中。
我想阅读这个问题有点复杂,但如果你看到一个例子可能更容易理解:
#include <vector>
#include <iostream>
using namespace std;
struct B
int n;
double val;
;
struct A
vector<B> v;
;
int main()
// generate some dummy data:
A a1;
a1.v.push_back(B1, 1.0);
a1.v.push_back(B2, 2.0);
a1.v.push_back(B1, 3.5);
A a2;
a1.v.push_back(B2, 2.0);
a1.v.push_back(B3, 1.0);
a1.v.push_back(B1, 2.5);
// this is my initial situation: a vector of type A
vector<A> va;
va.push_back(a1);
va.push_back(a2);
// what I want to get is a vector of type B with all B values whose n == 1
vector<B> vb;
// possible solution to get all elements of B
for(auto &any : va)
for(auto &b : any.v)
if(b.n == 1)
vb.push_back(b);
for(const auto &any:vb)
cout << any.val << endl;
return 0;
问题:
我想获得B
类的向量而不复制每个元素。稍后,我需要修改向量B
中的元素,它们也应该在向量A
中修改(意思是,如果我更改vb[0].val = 100;
,则va[0].v[0]
中的条目也应该是100
;
我考虑过使用指针来执行此操作,但我不确定这是否是个好主意,因为向量 va
的元素是在堆栈上而不是在堆上分配的。
** 编辑:**
我可以确保只要我需要B
的向量,我就不会以任何方式修改A
的向量。
【问题讨论】:
对不起,您无法避免复制。 C++ 不能以这种方式工作。您也许可以创建一个std::reference_wrapper
s 的向量,并可能完成类似的事情,但是,就拥有B
s 的向量而言,没有办法避免复制。这也许是 Java 中对象的工作方式,但 C++ 不是 Java。
"因为向量 va 的元素是在栈上而不是在堆上分配的。" 向量元素永远不会在栈上。也许这就是你的误解:在va.push_back(a1);
之后,向量包含a1
的副本,但不是a1
本身
除了@idclev463035818 的评论:当您使用非拥有指针时,对象的存储类是什么并不重要。它们总是可以使用的。
...但考虑到指向元素的指针是不稳定的。即,如果您在向量中添加/删除某些内容,它们就会失效
稍后,我需要修改向量 B 中的元素,它们也应该在向量 A 中进行修改 你为什么不在需要的时候直接 for(auto &any : va) for(auto &b : any.v) if(b.n == 1) do thing here
呢?
【参考方案1】:
在堆栈上有va
不一定是问题,只要您将va
移动到它的新位置。在这种情况下,您的“移动到”向量将在内部指向由“移动自”va
向量分配的存储空间(并且原始的 va
将变为空)。
但有一个问题是,如果您在已经创建了指向 B
s 的指针之后将新的 B
s 添加到 A
。如果A
的v
向量没有足够的容量来存储新插入的B
,则它必须将其所有B
元素重新分配到新的存储位置。这反过来又会改变所有B
元素的地址。
因此,只要您能保证,您就可以创建指针:
va
被移动而不是被复制。
初始化后没有新的B
s 被添加到A
。
假设以上可以保证,你可以创建你的vb
如下:
std::vector<B *> vb;
for (auto & a : va)
for (auto & b : a.v)
if (b.n == 1) vb.push_back(&b);
或使用std::reference_wrapper
:
std::vector<std::reference_wrapper<B>> vb;
for (auto & a : va)
std::copy_if(a.v.begin(), a.v.end(),
std::back_inserter(vb),
[](auto const & b) return b.n == 1; );
【讨论】:
nitpick,将 std::for_each 与 std::copy_if 结合使用会使这变得完美。 我个人更喜欢这里的范围。看起来更干净恕我直言。 谢谢,我可以确保在需要vb
时不再修改va
@theWiseBro 我个人认为std::for_each
不会比使用基于范围的 for 循环增加任何额外的可读性,但我理解你为什么要在这里使用它。
似乎并不完全清楚移动向量不会使迭代器无效,请参阅LWG issue 2321(如果分配器的propagate_on_container_move_assignment
是false
,它肯定会失效)。 以上是关于选择和修改嵌套向量中的条目的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章