在迭代向量时附加到向量?
我有一个正在迭代的向量。在迭代时,我可能会向向量附加新值。它看起来像:
struct Foo
{
bool condition;
};
void AppendToVec(vector<Foo>& v)
{
...
v.push_back(...);
}
vector<Foo> vec;
...
for (vector<Foo>::size_type i = 0; i < vec.size(); ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
这工作得很好,实际上优雅地处理了新添加的元素递归地需要添加更多元素的情况,但感觉有点脆弱。如果其他人出现并调整循环,它很容易被打破。例如:
//No longer iterates over newly appended elements
vector<Foo>::size_type size = vec.size();
for (vector<Foo>::size_type i = 0; i < size; ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
或者
//Vector resize may invalidate iterators
for (vector<Foo>::iterator i = vec.begin(); i != vec.end(); ++i)
{
if (vec->condition) AppendToVec(vec);
}
是否有处理此类案例的最佳实践?用“警告:此循环在迭代时有意附加到向量。谨慎更改”来评论循环是最好的方法吗?如果这能让事情变得更加健壮,我也愿意切换容器。
I have a vector that I am iterating over. While iterating, I may append new values to the vector. It looks something like:
struct Foo
{
bool condition;
};
void AppendToVec(vector<Foo>& v)
{
...
v.push_back(...);
}
vector<Foo> vec;
...
for (vector<Foo>::size_type i = 0; i < vec.size(); ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
This works fine, and in fact elegantly handles the case where the newly appended elements recursively require even more elements to be added, but it feels a little fragile. If someone else comes along and tweaks the loop, it can easily be broken. For example:
//No longer iterates over newly appended elements
vector<Foo>::size_type size = vec.size();
for (vector<Foo>::size_type i = 0; i < size; ++i)
{
if (vec[i].condition) AppendToVec(vec);
}
or
//Vector resize may invalidate iterators
for (vector<Foo>::iterator i = vec.begin(); i != vec.end(); ++i)
{
if (vec->condition) AppendToVec(vec);
}
Are there any best practices to handle cases like this? Is commenting the loop with a "Warning: This loop is intentionally appends to the vector while iterating. Change cautiously" the best approach? I am open to switching containers too if that makes things more robust.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我解决这个问题的方法通常是创建一个队列,向其中添加任何新元素,然后在迭代原始容器之后,处理队列中的元素和/或将它们附加到原始容器中。
这种方法的优点是,发生的事情是显而易见的,并且它适用于多个线程可能对新元素进行排队的情况。
My approach to this problem is often to create a queue to which I add any new elements, and then after iterating over the original container, process the elements in the queue and/or append them to the original.
The good points to this approach are that what is going on is obvious, and it works in scenarios where multiple threads could be enqueuing new elements.
那么不要使用
for
循环,而是使用while
循环。对我来说,for
循环始终意味着使用计数器的简单迭代循环。但是,如果我遇到while
循环,我觉得事情一定太复杂了,无法用简单的for
循环来表达它们。我会仔细观察,并且对“优化”while
循环比“优化”for
循环更加小心。Then don't use a
for
loop, use awhile
loop instead. For me, afor
loop always implies a simple, iterative loop using a counter. However, if I encounter awhile
loop, I feel like things must have been too complicated to to express them in a simplefor
loop. I will look closer and I'm more careful with "optimizing"while
loops than withfor
loops.如果已使用旧向量中的相对位置重新分配了
vec
(i-vec.begin( )
)。代码:
输出:
Allow
AppendToVec
to updatei
ifvec
has been reallocated by using the relative position in the old vector (i-vec.begin()
).Code:
Output:
正如已经指出的,正如您所感觉到的,这里存在一个问题。不过,您有多种可能的解决方案,因此不要盲目充电。
WARNING
标记或您的编码标准所需的任何内容,以警告未来的维护人员其中涉及欺骗。最好不要使用,但可以工作。reserve
并完全防止重新分配。std::deque
。大多数性能特征都是相似的,但是您可以在不使迭代器/引用等无效的情况下预先添加和附加新值...看起来很自然地适合这里我认为
deque
是这里更好的解决方案。它适合您的算法,您不必担心这些问题。您可能可以用deque
替换代码中的大部分vector
。如果您不想更改接口:vector
复制到deque
deque
内容分配到vector
不会涉及比重新分配
向量
两次更多的副本。所以随意吧!As has been pointed out, and as you sensed, there is an issue here. You have several possible solutions though, so don't charge blindly.
WARNING
tag or whatever your coding standard requires, to warn future maintainer that there is a trickery involved. Best not used, but could work.reserve
and prevent reallocation altogether.std::deque
. Most of the performance characteristics are similar, however you can prepend and append new values without invalidating iterators / references etc... looks like the natural fit hereI think that
deque
is the better solution here. It fits your algorithm and you don't have to worry about the issues. You could probably replace most of thevector
in your code bydeque
. And if you don't want to change the interface:vector
into thedeque
deque
content into thevector
won't involve much more copies than just reallocating the
vector
twice. So feel free!内联注释通常足以阐明这一点,例如
An inline comment is often enough to make this clear, e.g.
您需要随机访问该向量吗?如果不是,
std::list
更合适。我个人认为,在迭代向量时附加到向量并不是一个好主意。Do you need random access to this vector? If no, an
std::list
is more appropriate. It's my personal opinion that appending to a vector while iterating over it isn't a good idea.您可以追加到双端队列,而无需使其元素的迭代器无效。随机访问的效率接近向量,因此通常是替代它的不错选择。
You can append to a deque without invalidating iterators to its elements. Random access is close to the efficiency of vector, so its often a good choice to replace it.
和原来的没有太大区别。只是在这里明确一些事情:
Not too different from the original. Just making a few things explicit here: