C++ 在向量上迭代时追加向量?

C++ 在向量上迭代时追加向量?,c++,stl,C++,Stl,我有一个我正在迭代的向量。在迭代过程中,我可以向向量添加新值。它看起来像: 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

我有一个我正在迭代的向量。在迭代过程中,我可以向向量添加新值。它看起来像:

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);
}
structfoo
{
布尔条件;
};
无效附录向量(向量和v)
{
...
v、 向后推(…);
}
向量向量机;
...
对于(vector::size_type i=0;i
这很好,事实上,它优雅地处理了新添加的元素递归地需要添加更多元素的情况,但感觉有点脆弱。如果其他人出现并调整循环,它很容易被打破。例如:

//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::size_type size=vec.size();
对于(向量::大小\类型i=0;i

//向量大小调整可能使迭代器无效
对于(向量::迭代器i=vec.begin();i!=vec.end();++i)
{
如果(向量->条件)追加向量(向量);
}

有没有处理此类案件的最佳做法?用“警告:此循环在迭代时有意附加到向量。谨慎更改”来注释循环是最佳方法吗?我也愿意交换容器,如果这能让事情变得更稳健。

你需要随机访问这个向量吗?如果否,则更适合使用
std::list
。我个人认为,在对向量进行迭代时附加到向量不是一个好主意。

允许
AppendToVec
更新
i
,前提是
vec
已使用旧向量中的相对位置(
i-vec.begin()
)重新分配

代码:
我解决这个问题的方法通常是创建一个队列,向其中添加任何新元素,然后在迭代原始容器之后,处理队列中的元素和/或将它们附加到原始容器中

这种方法的优点在于所发生的事情是显而易见的,并且它可以在多个线程可以将新元素排队的场景中工作

如果其他人出现并调整循环,它很容易被打破


然后不要对
循环使用
,而是在
循环时使用
。对我来说,For
循环总是意味着使用计数器的简单迭代循环。然而,如果我遇到
while
循环,我觉得事情一定太复杂了,无法用简单的
for
循环来表达它们。我会更仔细地看,我会更仔细地“优化”while
循环,而不是
for
循环

您可以在不使迭代器失效的情况下将迭代器附加到deque中。随机存取接近于向量的效率,因此它往往是一个很好的替代选择

正如已经指出的,正如你所感觉到的,这里有一个问题。不过,你有几种可能的解决方案,所以不要盲目收费

  • 在循环的顶部有一个大而肥的注释,带有
    WARNING
    标记或您的编码标准要求的任何内容,以警告未来的维护人员,其中涉及到一个诡计。最好不要使用,但可以工作
  • 如果您能够提前知道将添加多少元素(或者您有一个相对严格的上限),那么可以使用
    reserve
    并完全防止重新分配
  • 使用
    std::deque
    。大多数性能特征是相似的,但是您可以在不使迭代器/引用无效的情况下预先添加和附加新值。。。看起来很适合这里
  • 使用单独的队列和双循环
我认为
deque
是这里更好的解决方案。它符合您的算法,您不必担心这些问题。您可以用
deque
替换代码中的大部分
向量。如果您不想更改接口:

  • 向量
    复制到
    deque
  • 计算
  • deque
    内容分配到
    向量中
除了两次重新分配
向量
之外,不会涉及更多的拷贝。所以请放心

void treat(std::vector<int>& vec)
{
   // WARNING: we use a deque because of its iterator invalidation properties
   std::deque<int> deq(vec.begin(), vec.end());

   for (std::deque<int>::const_iterator it = deq.begin(); it != deq.end(); ++it)
   {
     if (it->condition()) deq.push_back(func(*it));
   }

   vec.assign(deq.begin(), deq.end());
}
无效处理(标准::向量和向量) { //警告:我们使用deque是因为它的迭代器无效属性 std::dequedeq(vec.begin(),vec.end()); for(std::deque::const_迭代器it=deq.begin();it!=deq.end();++it) { 如果(it->condition())请求返回(func(*it)); } 向量赋值(deq.begin(),deq.end()); }
与原始版本没有太大区别。在这里,我只想明确一些事情:

for (vector<Foo>::size_type i = 0, n = vec.size(); i < n; ++i)
  if (vec[i].condition){
    AppendToVec(vec);
    n = vec.size();
  }
for(vector::size_type i=0,n=vec.size();i
内联注释通常足以说明这一点,例如:

for (vector<Foo>::size_type i = 0; i < vec.size() /* size grows in loop! */; ++i)
{
   if (vec[i].condition) AppendToVec(vec);
}
for(vector::size_type i=0;i
插入向量将使迭代器无效;这是没有办法的。你不能阻止人们编写破坏东西的代码。您最初的方法是最好的方法。我的回答绕过了这个问题。这正是我问这个问题的用例:防止后续开发人员破坏您的代码的唯一方法是使用单元测试(回归测试)。然后确保在将代码签入源代码管理之前运行并通过单元测试。我不确定迭代器
I
在附加某些内容后是否仍然有效。@Kristopher Johnson:如果向量重新分配,则不会。如果它不重新分配,他可能会侥幸逃脱。传递
i
的关键是使用相关信息在新向量中更新它。不要。更改容器内容将使迭代器无效,并可能导致程序崩溃。FWIW,我在Jacob为AppendToVec()添加新定义之前写了我的评论。如果他是我
void treat(std::vector<int>& vec)
{
   // WARNING: we use a deque because of its iterator invalidation properties
   std::deque<int> deq(vec.begin(), vec.end());

   for (std::deque<int>::const_iterator it = deq.begin(); it != deq.end(); ++it)
   {
     if (it->condition()) deq.push_back(func(*it));
   }

   vec.assign(deq.begin(), deq.end());
}
for (vector<Foo>::size_type i = 0, n = vec.size(); i < n; ++i)
  if (vec[i].condition){
    AppendToVec(vec);
    n = vec.size();
  }
for (vector<Foo>::size_type i = 0; i < vec.size() /* size grows in loop! */; ++i)
{
   if (vec[i].condition) AppendToVec(vec);
}