如何从STL容器中清除元素?
如何从STL容器中删除具有指定值或满足某些条件的元素?
对于不同种类的容器,是否有一种统一或统一的方法?
不幸的是,没有一个统一的接口或模式来清除STL容器中的元素。 但三种行为出现:
std ::vector模式
为了从std::vector
擦除满足某些条件的元素,一种常见的技术就是所谓的擦除删除习惯用法 ( erase-remove idiom) 。
如果v
是std::vector
一个实例,并且我们想从vector中擦除值为x
元素,那么可以使用如下代码:
// Erase elements having value "x" from vector "v" v.erase( std::remove(v.begin(), v.end(), x), v.end() );
如果擦除元素的条件要比简单的“要擦除的元素== x”更复杂,则可以使用std::remove_if()
algorithm来代替std::remove()
:
// Erase elements matching "erasing_condition" from vector "v" v.erase( std::remove_if(v.begin(), v.end(), erasing_condition), v.end() );
其中erasing_condition
是一元谓词,可以用几种forms表示:例如,它可以是一个以向量元素types作为input的bool
returns函数 (所以如果返回的值为true
,元素将从向量中删除;如果false
,它不会); 或者它可以在线表示为lambda ; 它可以是一个函数 ; 等等
( std::remove()
和std::remove_if()
都是<algorithm>
头文件中的通用algorithm。)
这里是来自Wikipedia的一个明确的解释:
algorithm
库为此提供了remove
和remove_if
algorithm。 由于这些algorithm对一系列由两个前向迭代器表示的元素进行操作,因此他们不知道底层容器或集合。 因此,没有元素实际上从容器中移除。 相反,所有不符合删除条件的元素都以相同的相对顺序汇集到范围的前面。 其余元素保持有效但未指定的状态。 当这完成后,remove
将返回一个迭代器,指向一个通过最后一个未清除的元素。为了实际消除容器中的元素,
remove
与容器的erase
成员函数结合,因此名称为“擦除 – 删除习惯用法”。
基本上, std::remove()
和std::remove_if()
将不满足擦除条件的元素压缩到范围的前面(即vector
的开头),然后erase()
实际上消除了剩余来自容器的元素。
这个模式也适用于其他容器,如std::deque
。
std :: list模式
要清除std::list
元素,可以使用简单的remove()
和remove_if()
方法 :
// Erase elements having value "x" from list "l" l.remove( x ) // Erase elements satisfying "erasing_condition" from list "l" l.remove_if( erasing_condition );
(其中, erasing_condition
是一个一元谓词,在上面的章节中为std::remove_if()
讨论了相同的特征。
相同的模式可以应用于类似的容器,如std::forward_list
。
关联容器(例如std :: map,std :: set,…)模式
像std::map
, std::set
, std::unordered_map
等联合容器遵循这里描述的常见模式:
-
如果擦除条件是简单的键匹配(即“擦除具有键x的元素” ),则可以调用简单的
erase()
方法 :// Erase element having key "k" from map "m": m.erase( k );
-
如果擦除条件比较复杂,并由一些自定义一元谓词表示(例如“擦除所有奇数元素” ),则可以使用
for
循环(在循环体中使用明确的擦除条件检查,并调用erase(iterator)
方法):// // 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; } }
需要一个统一的方法
从上面的分析可以看出,不幸的是,从STL容器中删除元素的方法并不是一致的。
下表总结了上述模式:
----------------+------------------------------------------ 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. ... | | ----------------+------------------------------------------
基于特定容器编写不同的特定代码容易出错,难以维护,难以阅读等。
但是,可以使用通用名称(例如, erase()
和erase_if()
)为不同的容器types重载函数模板,并将上述模式实现embedded到这些函数中。
因此,客户端可以简单地调用那些erase()
和erase_if()
generics函数,编译器会根据容器types将调用派发到适当的实现(在编译时 )。
Stephan T. Lavavej在这里介绍了一种更优雅的方法,使用模板元编程技术。