名稱空間
變體
操作

std::remove, std::remove_if

來自 cppreference.com
< cpp‎ | 演算法
 
 
演算法庫
有約束演算法與針對範圍的演算法 (C++20)
有約束的演算法,例如 ranges::copyranges::sort 等……
執行策略 (C++17)
排序及相關操作
劃分操作
排序操作
二分搜尋操作
(於已劃分範圍上)
集合操作(於已排序範圍上)
歸併操作(於已排序範圍上)
堆操作
最小/最大值操作
(C++11)
(C++17)
字典序比較操作
排列操作
C 庫
數值操作
未初始化記憶體上的操作
 
定義於標頭檔案 <algorithm>
(1)
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
(C++20 起為 constexpr)
(直到 C++26)
template< class ForwardIt, class T = typename std::iterator_traits

                                         <ForwardIt>::value_type >
constexpr ForwardIt remove( ForwardIt first, ForwardIt last,

                            const T& value );
(C++26 起)
(2)
template< class ExecutionPolicy, class ForwardIt, class T >

ForwardIt remove( ExecutionPolicy&& policy,

                  ForwardIt first, ForwardIt last, const T& value );
(C++17 起)
(直到 C++26)
template< class ExecutionPolicy, class ForwardIt,

          class T = typename std::iterator_traits
                        <ForwardIt>::value_type >
ForwardIt remove( ExecutionPolicy&& policy,

                  ForwardIt first, ForwardIt last, const T& value );
(C++26 起)
template< class ForwardIt, class UnaryPred >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPred p );
(3) (C++20 起為 constexpr)
template< class ExecutionPolicy, class ForwardIt, class UnaryPred >

ForwardIt remove_if( ExecutionPolicy&& policy,

                     ForwardIt first, ForwardIt last, UnaryPred p );
(4) (C++17 起)

從範圍 [firstlast) 中移除所有滿足特定條件的元素,並返回指向新範圍末尾的迭代器。

1) 移除所有等於 value 的元素(使用 operator==)。
3) 移除所有謂詞 p 返回 true 的元素。
2,4)(1,3),但按 policy 執行。
僅當滿足所有以下條件時,這些過載才參與過載決議

std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>true

(C++20 前)

std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>true

(C++20 起)


如果 `ForwardIt` 的值型別不是可複製賦值,則行為未定義。

(C++11 前)

如果 *first 的型別不是可移動賦值,則行為未定義。

(C++11 起)

目錄

[edit] 說明

移除操作透過移動範圍中的元素來完成,使得不被移除的元素出現在範圍的開頭。

  • 移動透過複製賦值(C++11 前)移動賦值(C++11 起)完成。
  • 移除操作是穩定的:不被移除的元素的相對順序保持不變。
  • 透過移除操作,[firstlast) 的底層序列不會縮短。給定返回的迭代器為 result
  • [resultlast) 的每個元素都具有有效但未指定的狀態,因為移動賦值可以透過從原本在該範圍內的元素中移動來消除元素。
(C++11 起)

[edit] 引數

first, last - 定義要處理元素的範圍的迭代器對
value - 要移除元素的值
policy - 要使用的 執行策略
p - 一元謂詞,如果元素應該被移除,則返回 true

對於型別為(可能是 const)`VT` 的每個引數 `v`,表示式 p(v) 必須可轉換為 bool,其中 `VT` 是 `ForwardIt` 的值型別,無論值類別如何,並且不得修改 `v`。因此,不允許引數型別為 VT&,除非對於 `VT` 移動等價於複製(C++11 起)

型別要求
-
ForwardIt 必須滿足 LegacyForwardIterator 的要求。
-
UnaryPredicate 必須滿足 Predicate 的要求。

[edit] 返回值

新值範圍的尾後迭代器(如果這不是 end,則它指向一個未指定的值,此迭代器與 end 之間的任何迭代器也如此)。

[edit] 複雜度

給定 Nstd::distance(first, last)

1,2) 使用 operator== 進行恰好 N 次比較。
3,4) 恰好 N 次謂詞 p 的應用。

[edit] 異常

帶有模板引數 ExecutionPolicy 的過載按如下方式報告錯誤

  • 如果作為演算法一部分呼叫的函式執行時丟擲異常,並且 `ExecutionPolicy` 是標準策略之一,則呼叫 std::terminate。對於任何其他 `ExecutionPolicy`,行為由實現定義。
  • 如果演算法未能分配記憶體,則丟擲 std::bad_alloc

[edit] 可能的實現

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;
}

[edit] 注意

對 `remove` 的呼叫通常後跟對容器的 `erase` 成員函式的呼叫,以實際從容器中移除元素。這兩個呼叫共同構成了所謂的擦除-移除習語

同樣的效果也可以透過以下非成員函式實現:

(C++20 起)

名稱相似的容器成員函式 list::removelist::remove_ifforward_list::removeforward_list::remove_if 會擦除被移除的元素。

這些演算法不能與關聯容器(例如 std::setstd::map)一起使用,因為它們的迭代器型別不能解引用為可移動賦值型別(這些容器中的鍵不可修改)。

標準庫還在 <cstdio> 中定義了 std::remove 的一個過載,它接受 const char* 並用於刪除檔案。

由於 `std::remove` 透過引用獲取 value,如果它是對範圍 [firstlast) 中元素的引用,則可能出現意外行為。

特性測試 標準 特性
__cpp_lib_algorithm_default_value_type 202403 (C++26) 演算法的列表初始化 (1,2)

[edit] 示例

以下程式碼透過將所有非空格字元左移,然後擦除多餘的字元來刪除字串中的所有空格。這是擦除-移除習語的一個示例。

#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';
 
    // The spaces are removed from the string only logically.
    // Note, we use view, the original string is still not shrunk:
    std::cout << "3) " << std::quoted(std::string_view(str1.begin(), noSpaceEnd))
              << ", size: " << str1.size() << '\n';
 
    str1.erase(noSpaceEnd, str1.end());
    // The spaces are removed from the string physically.
    std::cout << "4) " << std::quoted(str1) << ", size: " << 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", size: 15
4) "QuickRedDog", size: 11
5) "JumpedOverALazyFox"

[edit] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
LWG 283 C++98 要求 TEqualityComparable,但
`ForwardIt` 的值型別並非總是 `T`
要求 `ForwardIt` 的值型別
可複製賦值

[edit] 參閱

複製一個範圍的元素,忽略那些滿足特定條件的元素
(函式模板) [edit]
移除一個範圍中的連續重複元素
(函式模板) [edit]
移除滿足特定標準的元素
(演算法函式物件)[edit]