std::remove, std::remove_if
| 在標頭 <algorithm> 定義
|
||
| (1) | ||
(C++20 起為 constexpr) (C++26 前) |
||
| |
(C++26 起) | |
| (2) | ||
| |
(C++17 起) (C++26 前) |
|
| |
(C++26 起) | |
| (3) | (C++20 起為 constexpr) |
|
| |
(4) | (C++17 起) |
從範圍 [first, last) 移除所有滿足特定判別標準的元素,並返回範圍新結尾的尾後迭代器。
operator== 比較)value 的元素。p 對於它返回 true 的元素。policy 執行。|
|
(C++20 前) |
|
|
(C++20 起) |
|
如果 |
(C++11 前) |
|
如果 |
(C++11 起) |
解釋
移除元素是通過將範圍內的元素移動位置,使得不需要被移除的元素會在範圍的開頭出現的方式實現的。
- 元素通過複製賦值(C++11 前)移動賦值(C++11 起)實現。
- 移除操作是穩定的:不需要被移除的元素的相對順序保持不變。
- 移除操作不會縮短
[first,last)的底層序列。給定result為返回的迭代器:
[result,last)中的所有迭代器仍然可解引用。
|
(C++11 起) |
參數
| first, last | - | 要處理的元素範圍的迭代器對 |
| value | - | 要移除的元素值 |
| policy | - | 所用的執行策略 |
| p | - | 如果應該移除元素則返回 true 的一元謂詞。對每個(可為 const 的) |
| 類型要求 | ||
-ForwardIt 必須滿足老式向前迭代器 (LegacyForwardIterator) 。
| ||
-UnaryPredicate 必須滿足謂詞 (Predicate) 。
| ||
返回值
新範圍的尾後迭代器(如果它不是 end,那麼它指向未指定值,而此迭代器與 end 之間的迭代器所指向的任何值也是這樣)。
複雜度
給定 N 為 std::distance(first, last):
operator== 進行比較。p。異常
擁有名為 ExecutionPolicy 的模板形參的重載按下列方式報告錯誤:
- 如果作為算法一部分調用的函數的執行拋出異常,且
ExecutionPolicy是標準策略之一,那麼調用 std::terminate。對於任何其他ExecutionPolicy,行為由實現定義。 - 如果算法無法分配內存,那麼拋出 std::bad_alloc。
可能的實現
| remove (1) |
|---|
template<class ForwardIt, class T = typename std::iterator_traits<ForwardIt>::value_type>
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for (ForwardIt i = first; ++i != last;)
if (!(*i == value))
*first++ = std::move(*i);
return first;
}
|
| remove_if (3) |
template<class ForwardIt, class UnaryPred>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPred p)
{
first = std::find_if(first, last, p);
if (first != last)
for (ForwardIt i = first; ++i != last;)
if (!p(*i))
*first++ = std::move(*i);
return first;
}
|
註解
調用 remove 之後通常跟隨調用容器的 erase 成員函數來從容器中實際移除元素。這兩個調用一併被稱為擦除移除手法。
|
以下非成員函數也可以達到相同的效果: |
(C++20 起) |
同名的容器成員函數 list::remove、list::remove_if、forward_list::remove 和 forward_list::remove_if 擦除被移除的元素。
這些算法通常不能用於如 std::set 與 std::map 的關聯容器,因為它們的迭代器類型並不解引用為可移動賦值 (MoveAssignable) 類型(這些容器中的鍵不可修改)。
標準庫也在 <cstdio> 中定義了一個 std::remove 重載,它接收的是 const char* 並用於刪除文件。
因為 std::remove 以引用接收 value,如果它引用了範圍 [first, last) 中的元素,那麼它可能有預期外的行為。
| 功能特性測試宏 | 值 | 標準 | 功能特性 |
|---|---|---|---|
__cpp_lib_algorithm_default_value_type |
202403 |
(C++26) | 算法中的列表初始化 (1,2) |
示例
下列代碼從字符串移除所有空格,通過遷移所有非空格字符到左側,再擦除其他內容。這是擦除移除手法的樣例。
#include <algorithm>
#include <cassert>
#include <cctype>
#include <complex>
#include <iomanip>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
int main()
{
std::string str1{"Quick Red Dog"};
std::cout << "1) " << std::quoted(str1) << '\n';
const auto noSpaceEnd = std::remove(str1.begin(), str1.end(), ' ');
std::cout << "2) " << std::quoted(str1) << '\n';
// 空格只是逻辑上从字符串被移除。
// 通过视图可以发现原始字符串并没有缩小:
std::cout << "3) " << std::quoted(std::string_view(str1.begin(), noSpaceEnd))
<< ",大小:" << str1.size() << '\n';
str1.erase(noSpaceEnd, str1.end());
// 物理移除字符串中的空格。
std::cout << "4) " << std::quoted(str1) << ",大小:" << str1.size() << '\n';
std::string str2 = "Jumped\n Over\tA\vLazy \t Fox\r\n";
str2.erase(std::remove_if(str2.begin(),
str2.end(),
[](unsigned char x){ return std::isspace(x); }),
str2.end());
std::cout << "5) " << std::quoted(str2) << '\n';
std::vector<std::complex<double>> nums{{2, 2}, {1, 3}, {4, 8}};
#ifdef __cpp_lib_algorithm_default_value_type
nums.erase(std::remove(nums.begin(), nums.end(), {1, 3}), nums.end());
#else
nums.erase(std::remove(nums.begin(), nums.end(), std::complex<double>{1, 3}),
nums.end());
#endif
assert((nums == std::vector<std::complex<double>>{{2, 2}, {4, 8}}));
}
輸出:
1) "Quick Red Dog"
2) "QuickRedDog Dog"
3) "QuickRedDog",大小:15
4) "QuickRedDog",大小:11
5) "JumpedOverALazyFox"
缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
| 缺陷報告 | 應用於 | 出版時的行為 | 正確行為 |
|---|---|---|---|
| LWG 283 | C++98 | T 需要是可相等比較 (EqualityComparable) 的,但是 ForwardIt 的值類型不一定是 T
|
改成要求 ForwardIt 的值類型是可複製賦值 (CopyAssignable) 的 |
參閱
| 複製範圍並忽略滿足特定條件的元素 (函數模板) | |
| 移除範圍中連續重複元素 (函數模板) | |
(C++20)(C++20) |
移除滿足特定條件的元素 (算法函數對象) |