从向量中删除项目,而在 C++11 范围内的“for”循环中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10360461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:56:58  来源:igfitidea点击:

Removing item from vector, while in C++11 range 'for' loop?

c++vectorfor-loopc++11

提问by EddieV223

I have a vector of IInventory*, and I am looping through the list using C++11 range for, to do stuff with each one.

我有一个 IInventory* 向量,我正在使用 C++11 范围循环遍历列表,以对每个列表进行处理。

After doing some stuff with one, I may want to remove it from the list and delete the object. I know I can call deleteon the pointer any time to clean it up, but what is the proper way to remove it from the vector, while in the range forloop? And if I remove it from the list will my loop be invalidated?

在对一个做了一些事情之后,我可能想将它从列表中删除并删除该对象。我知道我可以随时调用delete指针来清理它,但是在范围for循环中将它从向量中删除的正确方法是什么?如果我从列表中删除它,我的循环会失效吗?

std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());

for (IInventory* index : inv)
{
    // Do some stuff
    // OK, I decided I need to remove this object from 'inv'...
}

采纳答案by Seth Carnegie

No, you can't. Range-based foris for when you need to access each element of a container once.

不,你不能。Range-basedfor适用于需要访问容器的每个元素一次的情况。

You should use the normal forloop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.

for如果您需要在进行过程中修改容器、多次访问元素或以非线性方式遍历容器,则应使用普通循环或其同类循环。

For example:

例如:

auto i = std::begin(inv);

while (i != std::end(inv)) {
    // Do some stuff
    if (blah)
        i = inv.erase(i);
    else
        ++i;
}

回答by Branko Dimitrijevic

Every time an element is removed from the vector, you must assume the iterators at or after the erased element are no longer valid, because each of the elements succeeding the erased element are moved.

每次从向量中删除一个元素时,您必须假设被擦除元素处或之后的迭代器不再有效,因为被擦除元素之后的每个元素都被移动了。

A range-based for-loop is just syntactic sugar for "normal" loop using iterators, so the above applies.

基于范围的 for 循环只是使用迭代器的“正常”循环的语法糖,因此上述适用。

That being said, you could simply:

话虽如此,您可以简单地:

inv.erase(
    std::remove_if(
        inv.begin(),
        inv.end(),
        [](IInventory* element) -> bool {
            // Do "some stuff", then return true if element should be removed.
            return true;
        }
    ),
    inv.end()
);

回答by dirkgently

You ideally shouldn't modify the vector while iterating over it. Use the erase-remove idiom. If you do, you're likely to encounter a few issues. Since in a vectoran eraseinvalidates all iterators beginning with the element being erased upto the end()you will need to make sure that your iterators remain valid by using:

理想情况下,您不应在迭代时修改向量。使用擦除-删除习语。如果这样做,您可能会遇到一些问题。由于在vector一个erase会使所有迭代器与元素开始被擦除高达的end(),你需要确保你的迭代器仍然有效使用:

for (MyVector::iterator b = v.begin(); b != v.end();) { 
    if (foo) {
       b = v.erase( b ); // reseat iterator to a valid value post-erase
    else {
       ++b;
    }
}

Note, that you need the b != v.end()test as-is. If you try to optimize it as follows:

请注意,您需要按b != v.end()原样进行测试。如果您尝试按如下方式对其进行优化:

for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)

you will run into UB since your eis invalidated after the first erasecall.

您将遇到 UB,因为您e在第一次erase调用后无效。

回答by Yexo

Is it a strict requirement to remove elements while in that loop? Otherwise you could set the pointers you want to delete to NULL and make another pass over the vector to remove all NULL pointers.

在该循环中删除元素是否有严格要求?否则,您可以将要删除的指针设置为 NULL 并再次遍历向量以删除所有 NULL 指针。

std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );

for ( IInventory* &index : inv )
{
    // do some stuff
    // ok I decided I need to remove this object from inv...?
    if (do_delete_index)
    {
        delete index;
        index = NULL;
    }
}
std::remove(inv.begin(), inv.end(), NULL);

回答by Jayhello

I will show with example, the below example remove odd elements from vector:

我将举例说明,下面的例子从向量中删除奇数元素:

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};

    //method 1
    for(auto it = vecInt.begin();it != vecInt.end();){
        if(*it % 2){// remove all the odds
            it = vecInt.erase(it);
        } else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 2
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 2
    for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
        if (*it % 2){
            it = vecInt.erase(it);
        }else{
            ++it;
        }
    }

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

    // recreate vecInt, and use method 3
    vecInt = {0, 1, 2, 3, 4, 5};
    //method 3
    vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
                 [](const int a){return a % 2;}),
                 vecInt.end());

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;

}

output aw below:

输出aw如下:

024
024
024

Keep in mind, the method erasewill return the next iterator of the passed iterator.

请记住,该方法erase将返回传递的迭代器的下一个迭代器。

From here , we can use a more generate method:

这里,我们可以使用更多的生成方法:

template<class Container, class F>
void erase_where(Container& c, F&& f)
{
    c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
            c.end());
}

void test_del_vector(){
    std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
    //method 4
    auto is_odd = [](int x){return x % 2;};
    erase_where(vecInt, is_odd);

    // output all the remaining elements
    for(auto const& it:vecInt)std::cout<<it;
    std::cout<<std::endl;    
}

See here to see how to use std::remove_if. https://en.cppreference.com/w/cpp/algorithm/remove

请参阅此处了解如何使用std::remove_if. https://en.cppreference.com/w/cpp/algorithm/remove

回答by nikc

In opposition to this threads title, I'd use two passes:

与此主题标题相反,我将使用两个通行证:

#include <algorithm>
#include <vector>

std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());

std::vector<IInventory*> toDelete;

for (IInventory* index : inv)
{
    // Do some stuff
    if (deleteConditionTrue)
    {
        toDelete.push_back(index);
    }
}

for (IInventory* index : toDelete)
{
    inv.erase(std::remove(inv.begin(), inv.end(), index), inv.end());
}

回答by Aconcagua

OK, I'm late, but anyway: Sorry, not correct what I read so far - it ispossible, you just need two iterators:

好的,我迟到了,但无论如何:对不起,我目前阅读的内容不正确 -可能,您只需要两个迭代器:

std::vector<IInventory*>::iterator current = inv.begin();
for (IInventory* index : inv)
{
    if(/* ... */)
    {
        delete index;
    }
    else
    {
        *current++ = index;
    }
}
inv.erase(current, inv.end());

Just modifying the value an iterator points to does not invalidate any other iterator, so we can do this without having to worry. Actually, std::remove_if(gcc implementation at least) does something very similar (using a classic loop...), just does not delete anything and does not erase.

仅修改迭代器指向的值不会使任何其他迭代器失效,因此我们可以不必担心。实际上,std::remove_if(至少是 gcc 实现)做了一些非常相似的事情(使用经典循环......),只是不删除任何内容也不擦除。

Be aware, however, that this is not thread safe(!) - however, this applies, too, for some of the other solutions above...

但是请注意,这不是线程安全的(!) - 但是,这也适用于上面的其他一些解决方案......

回答by lilbigwill99

sorry for necroposting and also sorry if my c++ expertise gets in the way of my answer, but if you trying to iterate through each item and make possible changes (like erasing an index), try using a backwords for loop.

对不起,如果我的 C++ 专业知识妨碍了我的回答,我很抱歉,但是如果您尝试遍历每个项目并进行可能的更改(例如擦除索引),请尝试使用 backwords for 循环。

for(int x=vector.getsize(); x>0; x--){

//do stuff
//erase index x

}

when erasing index x, the next loop will be for the item "in front of" the last iteration. i really hope this helped someone

擦除索引 x 时,下一个循环将针对最后一次迭代“前面”的项目。我真的希望这对某人有所帮助

回答by ajpieri

I think I would do the following...

我想我会做以下...

for (auto itr = inv.begin(); itr != inv.end();)
{
   // Do some stuff
   if (OK, I decided I need to remove this object from 'inv')
      itr = inv.erase(itr);
   else
      ++itr;
}

回答by suraj kumar

you can't delete the iterator during the loop iteration because iterator count get mismatch and after some iteration you would have invalid iterator.

您不能在循环迭代期间删除迭代器,因为迭代器计数不匹配,并且在一些迭代之后您将拥有无效的迭代器。

Solution: 1) take the copy of original vector 2) iterate the iterator using this copy 2) do some stuff and delete it from original vector.

解决方案:1)获取原始向量的副本 2)使用此副本迭代迭代器 2)做一些事情并将其从原始向量中删除。

std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());

std::vector<IInventory*> copyinv = inv;
iteratorCout = 0;
for (IInventory* index : copyinv)
{
    // Do some stuff
    // OK, I decided I need to remove this object from 'inv'...
    inv.erase(inv.begin() + iteratorCout);
    iteratorCout++;
}