使用 erase-remove_if 成语
Using erase-remove_if idiom
假设我有 std::vector<std::pair<int,Direction>>
.
我正在尝试使用 erase-remove_if 习语从向量中删除对。
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }));
我想删除所有将 .first 值设置为 4 的对。
在我的示例中,我有对:
- 4, Up
- 4, Down
- 2, Up
- 6, Up
然而,在我执行 erase-remove_if 之后,我剩下:
- 2, Up
- 6, Up
- 6, Up
我做错了什么?
正确的代码是:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());
您需要删除从std::remove_if
返回的迭代器开始到向量末尾的范围,而不仅仅是单个元素。
“为什么?”
std::remove_if
交换向量内的元素,以便将 所有与谓词不匹配的元素放在容器 的开头。这意味着如果谓词(lambda 函数的主体)returns true
,则该元素将放置在向量的 end 处。
remove_if
然后 **returns 一个指向第一个与谓词匹配的元素的迭代器 **。换句话说,指向要删除的第一个元素的迭代器。
std::vector::erase
擦除从返回的迭代器开始到向量结尾的范围,这样所有元素匹配谓词被删除.
方法 std::vector::erase
有两个重载:
iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );
第一个仅删除 pos
处的元素,而第二个删除范围 [first, last)
.
由于您在调用中忘记了 last
迭代器,因此第一个版本由重载决议选择,并且您只删除了移动到末尾 std::remove_if
的第一对。您需要这样做:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool {
return stopPoint.first == 4;
}),
stopPoints.end());
erase-remove 习语的工作原理如下。假设你有一个向量 {2, 4, 3, 6, 4}
并且你想删除 4
:
std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);
将通过将“删除的”值放在末尾来将向量转换为 {2, 3, 6, A, B}
(末尾的值 A
和 B
未指定(就好像该值是moved),这就是为什么你的例子中有 6
) 和 return 到 A
的迭代器(“已删除”值的第一个).
如果你这样做:
vec.erase(it)
...选择 std::vector::erase
的第一个重载,您只删除 it
处的值,即 A
并得到 {2, 3, 6, B}
.
通过添加第二个参数:
vec.erase(it, vec.end())
...选择第二个重载,您擦除 it
和 vec.end()
之间的值,因此 A
和 B
都被擦除。
假设我有 std::vector<std::pair<int,Direction>>
.
我正在尝试使用 erase-remove_if 习语从向量中删除对。
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }));
我想删除所有将 .first 值设置为 4 的对。
在我的示例中,我有对:
- 4, Up
- 4, Down
- 2, Up
- 6, Up
然而,在我执行 erase-remove_if 之后,我剩下:
- 2, Up
- 6, Up
- 6, Up
我做错了什么?
正确的代码是:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());
您需要删除从std::remove_if
返回的迭代器开始到向量末尾的范围,而不仅仅是单个元素。
“为什么?”
std::remove_if
交换向量内的元素,以便将 所有与谓词不匹配的元素放在容器 的开头。这意味着如果谓词(lambda 函数的主体)returnstrue
,则该元素将放置在向量的 end 处。remove_if
然后 **returns 一个指向第一个与谓词匹配的元素的迭代器 **。换句话说,指向要删除的第一个元素的迭代器。std::vector::erase
擦除从返回的迭代器开始到向量结尾的范围,这样所有元素匹配谓词被删除.
方法 std::vector::erase
有两个重载:
iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );
第一个仅删除 pos
处的元素,而第二个删除范围 [first, last)
.
由于您在调用中忘记了 last
迭代器,因此第一个版本由重载决议选择,并且您只删除了移动到末尾 std::remove_if
的第一对。您需要这样做:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool {
return stopPoint.first == 4;
}),
stopPoints.end());
erase-remove 习语的工作原理如下。假设你有一个向量 {2, 4, 3, 6, 4}
并且你想删除 4
:
std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);
将通过将“删除的”值放在末尾来将向量转换为 {2, 3, 6, A, B}
(末尾的值 A
和 B
未指定(就好像该值是moved),这就是为什么你的例子中有 6
) 和 return 到 A
的迭代器(“已删除”值的第一个).
如果你这样做:
vec.erase(it)
...选择 std::vector::erase
的第一个重载,您只删除 it
处的值,即 A
并得到 {2, 3, 6, B}
.
通过添加第二个参数:
vec.erase(it, vec.end())
...选择第二个重载,您擦除 it
和 vec.end()
之间的值,因此 A
和 B
都被擦除。