如何从STL容器中删除元素,具有指定的值,或满足某些条件?
对于不同种类的容器,是否有单一的通用或统一的方法?
答案 0 :(得分:54)
不幸的是,没有一个统一接口或模式可以从STL容器中删除元素。 但是出现了三种行为:
要从 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()
算法代替{{1 }}:
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
- 返回函数将vector元素类型作为输入(因此,如果返回的值为bool
,则元素将从向量中删除;如果是{ {1}},它不会);或者可以将 in-line 表示为 lambda ;它可以是functor;等
(true
和false
都是来自std::remove()
标头的通用算法。)
以下是明确的解释from Wikipedia:
std::remove_if()
库提供<algorithm>
和algorithm
算法。因为这些算法在一系列范围内运行 由两个前向迭代器表示的元素,他们不知道 底层容器或集合。因此,实际上没有元素 从容器中取出。相反,所有不适合的元素 删除标准汇集到范围的前面,在 相同的相对顺序。其余元素保留为有效,但是 未指明的状态。完成此操作后,remove
将返回一个迭代器 指出一个经过最后一个未被删除的元素。要实际消除容器中的元素,
remove_if
会合并 使用容器的remove
成员函数,因此得名 “擦除 - 删除成语”。
基本上,remove
和erase
会压缩不满足范围前面的删除条件的元素(即{{1}的开头) }),然后std::remove()
实际上从容器中删除了剩余的元素。
此模式也适用于 std::remove_if()
等其他容器。
要清除 vector
中的元素,可以使用简单的 erase()
和std::deque
方法:
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
等,遵循此处描述的常见模式:
如果擦除条件是简单的键匹配(即“擦除元素
使用键x“),可以调用一个简单的 std::map
方法:
std::set
如果擦除条件更复杂,并且由某些自定义表示
一元谓词(例如“擦除所有奇数元素”),然后可以使用std::unordered_map
循环
(在循环体中检查明确的擦除条件,并调用erase()
方法):
// Erase element having key "k" from map "m":
m.erase( k );
从上述分析可以看出,遗憾的是,没有一种统一的通用方法可以从STL容器中删除元素。
下表总结了上述模式:
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;
}
}
)重载,并将上述模式实现嵌入到这些函数中。
因此,客户端可以简单地调用那些----------------+------------------------------------------
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()
泛型函数,编译器将根据容器类型将调用分派给正确的实现(在编译时)。
使用模板元编程技术的更优雅的方法是by Stephan T. Lavavej here。