如何从STL容器中删除元素? [英] How do I erase elements from STL containers?

查看:119
本文介绍了如何从STL容器中删除元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何从STL容器中删除元素,具有指定的或满足某些条件



解决方案

不幸的是,没有一个用于从STL容器中擦除元素的单一统一接口或模式。
但是会出现三种行为:



std :: vector模式



来自 std :: vector 的某些条件,一种常见的技术是所谓的 erase-remove idiom



如果 v std :: vector ,我们要从向量中删除值为 x 的元素,可以使用这样的代码:

  //从向量v中删除值为x的元素
v.erase(std :: remove(v.begin(),v.end(),x) ,v.end());

如果擦除元素所满足的标准比简单的可以使用 std :: remove_if()算法代替 std :: remove() / code>:

  //从向量v中擦除匹配erasing_condition的元素
erase(std :: remove_if(v.begin(),v.end(),erasing_condition),v.end());

其中 erasing_condition 是一个一元谓词可以用几种形式表示:它可以是以向量元素类型作为输入的 bool - 返回函数(因此,如果返回值 true ,元素将从向量中删除;如果 false ,则不会);或者它可以作为 lambda 在线表示;它可以是 函子 ;



(两个 std :: remove() std :: remove_if )< algorithm> 头的通用算法。)



清楚解释维基百科


算法库提供 remove remove_if
的算法。因为这些算法在由两个前向迭代器表示的
元素的范围上操作,所以它们不知道底层容器或集合的
。因此,没有元素实际上从容器中删除
。相反,不符合
删除条件的所有元素都被放在范围的前面,在
相同的相对顺序中。其余元素保留在有效状态,但
未指定状态。当这样做时, remove 返回一个迭代器
指向一个过去的最后一个未删除的元素。



实际上从容器中删除元素, remove
与容器的擦除成员函数组合,


基本上, std :: remove code>和 std :: remove_if()将不满足擦除标准的元素压缩到范围的前面到向量的开头),然后 erase()实际上从容器中删除了剩余的元素。 p>

此模式也适用于其他容器,例如 std :: deque



std :: list模式



std :: list ,简单 remove() remove_if / strong>可用:

  //从列表l中删除值为x的元素
l。 remove(x)

//从列表l擦除满足erasing_condition的元素
l.remove_if(erasing_condition);

(其中 erasing_condition 是一元谓词,具有与上述 std :: remove_if()中讨论的相同的特征。



应用于类似的容器,例如 std :: forward_list



关联容器(例如std :: map,std :: set,...)Pattern



std :: map std :: set std :: unordered_map 等按照此处描述的常见模式:


  1. 如果擦除条件是简单的键匹配(即擦除元素
    具有键x
    ) ,则可以调用简单的 erase()方法

      //从地图m中删除具有键k的元素:
    m.erase(k);


  2. 如果擦除条件更复杂, (例如擦除所有奇数元素),则可以使用循环的
    (使用显式擦除条件检查在循环体中,并调用 erase(iterator)方法):

      // 
    //擦除来自关联容器c的所有元素,满足erasing_condition:
    //
    for(auto it = c.begin(); it! end(); / *it在循环体内更新* /)
    {
    if(erasing_condition(* it))
    {
    //擦除与指定条件
    //来自关联容器。
    it = c.erase(it);

    //注意:
    // erase()返回一个迭代器到元素
    //后面的最后一个元素删除,
    //所以我们可以继续从该位置开始的for循环迭代。
    }
    else
    {
    //当前元素_not_满足擦除条件,
    //因此我们可以直接移动到下一个元素。
    ++ it;
    }
    }




统一方法的需要



从上面的分析可以看出,遗憾的是没有一个统一的通用方法来从STL容器中删除元素。 p>

下表总结了上述模式:


  ---------------- + ------------------------------- ----------- 
Container |擦除模式
---------------- + --------------------------- ---------------
|
vector |使用erase-remove成语。
deque |
|
---------------- + ----------------------------- -------------
|
list |调用remove()/ remove_if()方法。
forward_list |
|
---------------- + ----------------------------- -------------
|
地图|简单擦除(key)方法调用,
set |或
unordered_map |循环通过容器,
multimap |和调用擦除(迭代器)匹配
|条件。
... |
|
---------------- + ----------------------------- -------------


基于特定容器编写不同的特定代码是容易出错,难以维护,难以阅读等等。



但是,可以使用通用名不同容器类型的 erase() erase_if())重载将上述模式实现嵌入到这些函数中。

因此,客户端可以简单地调用 erase() erase_if 通用函数,编译器将根据容器类型调度正确实现(在编译时)



一个更优雅的方法,使用模板元程序设计技术, Stephan T. Lavavej / p>

How do I erase elements from STL containers, having a specified value, or satisfying some condition?

Is there a single common or uniform way of doing that for different kinds of containers?

解决方案

Unfortunately, there isn't a single uniform interface or pattern for erasing elements from STL containers. But three behaviors emerge:

std::vector Pattern

To erase elements that fulfill a certain condition from a std::vector, a common technique is the so called erase-remove idiom.

If v is an instance of std::vector, and we want to erase elements with value x from the vector, code like this can be used:

// Erase elements having value "x" from vector "v"
v.erase( std::remove(v.begin(), v.end(), x), v.end() );

If the criterion to be fulfilled for erasing elements is more complex than the simple "element to be erased == x", the std::remove_if() algorithm can be used instead of std::remove():

// Erase elements matching "erasing_condition" from vector "v"
v.erase( std::remove_if(v.begin(), v.end(), erasing_condition), v.end() );

where erasing_condition is a unary predicate, that can be expressed in several forms: e.g. it can be a bool-returning function taking vector element type as input (so if the returned value is true, the element will be erased from the vector; if it's false, it won't); or it can be expressed in-line as a lambda; it can be a functor; etc.

(Both std::remove() and std::remove_if() are generic algorithms from <algorithm> header.)

Here is a clear explanation from Wikipedia:

The algorithm library provides the remove and remove_if algorithms for this. Because these algorithms operate on a range of elements denoted by two forward iterators, they have no knowledge of the underlying container or collection. Thus, no elements are actually removed from the container. Rather, all elements which don't fit the remove criteria are brought together to the front of the range, in the same relative order. The remaining elements are left in a valid, but unspecified state. When this is done, remove returns an iterator pointing one past the last unremoved element.

To actually eliminate elements from the container, remove is combined with the container's erase member function, hence the name "erase-remove idiom".

Basically, std::remove() and std::remove_if() compact the elements that do not satisfy the erasing criteria to the front of the range (i.e. to the beginning of the vector), and then erase() actually eliminates the remaining elements from the container.

This pattern applies also to other containers like std::deque.

std::list Pattern

To erase elements from a std::list, simple remove() and remove_if() methods are available:

// Erase elements having value "x" from list "l"
l.remove( x )

// Erase elements satisfying "erasing_condition" from list "l"
l.remove_if( erasing_condition );

(Where erasing_condition is a unary predicate, with the same characteristics discussed for std::remove_if() in the above section.)

The same pattern can be applied to similar containers, like std::forward_list.

Associative Containers (e.g. std::map, std::set, ...) Pattern

Associative containers like std::map, std::set, std::unordered_map, etc. follow the common pattern described here:

  1. If the erasing condition is a simple key-matching (i.e. "erase the element having key x"), then a simple erase() method can be called:

    // Erase element having key "k" from map "m":
    m.erase( k );
    

  2. If the erasing condition is more complex, and is expressed by some custom unary predicate (e.g. "erase all odd elements"), then a for loop can be used (with an explicit erasing condition checking in loop body, and call to erase(iterator) method):

    //
    // Erase all elements from associative container "c", satisfying "erasing_condition":
    //
    for (auto it = c.begin(); it != c.end(); /* "it" updated inside loop body */ )
    {
        if ( erasing_condition(*it) )
        {   
            // Erase the element matching the specified condition 
            // from the associative container.
            it = c.erase(it);
    
            // Note:
            // erase() returns an iterator to the element 
            // that follows the last element removed, 
            // so we can continue the "for" loop iteration from that position.
        }
        else
        {
            // Current element does _not_ satisfy erasing condition,
            // so we can just move on to the next element.
            ++it;
        }       
    }     
    

The Need for a Unified Approach

As it can be noted from the above analysis, unfortunately there isn't a uniform common approach for erasing elements from STL containers.

The following table summarizes the aforementioned patterns:

----------------+------------------------------------------             
   Container    |            Erasing Pattern
----------------+------------------------------------------                
                |
 vector         |    Use erase-remove idiom.
 deque          |
                |
----------------+------------------------------------------               
                |
 list           |    Call remove()/remove_if() methods.
 forward_list   |
                |
----------------+------------------------------------------  
                |
 map            |    Simple erase(key) method call, 
 set            |    or 
 unordered_map  |    loop through the container,
 multimap       |    and call erase(iterator) on matching
                |    condition.
 ...            |
                |
----------------+------------------------------------------

Writing different specific code based on the particular container is error-prone, hard to maintain, hard to read, etc.

However, it's possible to write function templates with common names (e.g. erase() and erase_if()) overloaded for different container types, and embed the aforementioned pattern implementations in those functions.
So, the client can simply call those erase() and erase_if() generic functions, and the compiler will dispatch the call to proper implementation (at compile time), based on container type.

A more elegant approach, using a template meta-programming technique, is presented by Stephan T. Lavavej here.

这篇关于如何从STL容器中删除元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆