不幸的是,没有一个单一的uniform 接口或模式可用于从 STL 容器中擦除元素。
但出现了三种行为:
std::vector 模式
要从 std::vector 中删除满足特定条件的元素,一种常见的技术是所谓的erase-remove idiom。
如果v 是std::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()算法代替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 是一元谓词,可以用多种形式表示:例如它可以是一个bool-returning函数,以向量元素类型为输入(因此如果返回值为true,则该元素将从向量中删除;如果是false,则惯于);或者它可以in-line表示为一个lambda;可以是functor;等等
(std::remove() 和 std::remove_if() 都是来自 <algorithm> 标头的通用算法。)
这里有明确的解释from Wikipedia:
algorithm 库提供了 remove 和 remove_if
为此的算法。因为这些算法在一系列
由两个前向迭代器表示的元素,它们不知道
底层容器或集合。因此,实际上没有元素
从容器中取出。相反,所有不适合的元素
删除条件被放在范围的前面,在
相同的相对顺序。其余元素保留为有效,但
未指定的状态。完成后,remove 返回一个迭代器
指向最后一个未删除的元素。
为了真正从容器中消除元素,remove 被组合在一起
使用容器的erase 成员函数,因此得名
“擦除删除习语”。
基本上,std::remove() 和std::remove_if() 将满足 擦除条件的元素压缩到范围的前面(即vector 的开头),然后@ 987654349@ 实际上是从容器中消除了剩余的元素。
这种模式也适用于其他容器,例如 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())重载的函数模板,并将上述模式实现嵌入到这些函数中。
因此,客户端可以简单地调用那些erase() 和erase_if() 泛型函数,编译器会根据容器类型将调用分派给正确的实现(在编译时)。
by Stephan T. Lavavej here 提出了一种更优雅的方法,使用模板元编程技术。