std::remove, std::remove_if

出自cppreference.com
 
 
算法庫
受約束算法及範圍上的算法 (C++20)
包含算法例如 ranges::copy, ranges::sort, ...
執行策略 (C++17)
排序和相關操作
劃分操作
排序操作
二分搜索操作(在已劃分範圍上)
集合操作(在有序範圍上)
歸併操作(在有序範圍上)
堆操作
最小/最大操作
(C++11)
(C++17)
字典序比較操作
排列操作
C 庫

數值運算
(C++11)                       
在未初始化內存上的操作
 
在標頭 <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) 移除所有等於(用 operator== 比較)value 的元素。
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值類型可複製賦值 (CopyAssignable) ,那麼行為未定義。

(C++11 前)

如果 *first 的類型不可移動賦值 (MoveAssignable) ,那麼行為未定義。

(C++11 起)

解釋

移除元素是通過將範圍內的元素移動位置,使得不需要被移除的元素會在範圍的開頭出現的方式實現的。

  • 元素通過複製賦值(C++11 前)移動賦值(C++11 起)實現。
  • 移除操作是穩定的:不需要被移除的元素的相對順序保持不變。
  • 移除操作不會縮短 [firstlast) 的底層序列。給定 result 為返回的迭代器:
  • [resultlast) 的所有元素都具有有效但未指定的狀態,這是因為移動賦值會通過將原來在該範圍內的元素移走的方式來銷毀元素。
(C++11 起)

參數

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

對每個(可為 const 的) VT 類型參數 v ,其中 VTForwardIt 的值類型,表達式 p(v) 必須可轉換到 bool,無關乎值類別,而且必須不修改 v 。從而不允許 VT& 類型參數,亦不允許 VT ,除非對 VT 而言移動等價於複製(C++11 起)。 ​

類型要求
-
ForwardIt 必須滿足老式向前迭代器 (LegacyForwardIterator)
-
UnaryPredicate 必須滿足謂詞 (Predicate)

返回值

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

複雜度

給定 Nstd::distance(first, last)

1,2) 應用 Noperator== 進行比較。
3,4) 應用 N 次謂詞 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 成員函數來從容器中實際移除元素。這兩個調用一併被稱為擦除移除手法

以下非成員函數也可以達到相同的效果:

  • std::erase,它對所有標準序列容器都有重載
  • std::erase_if,它對所有標準容器都有重載
(C++20 起)

同名的容器成員函數 list::removelist::remove_ifforward_list::removeforward_list::remove_if 擦除被移除的元素。

這些算法通常不能用於如 std::setstd::map 的關聯容器,因為它們的迭代器類型並不解引用為可移動賦值 (MoveAssignable) 類型(這些容器中的鍵不可修改)。

標準庫也在 <cstdio> 中定義了一個 std::remove 重載,它接收的是 const char* 並用於刪除文件。

因為 std::remove 以引用接收 value,如果它引用了範圍 [firstlast) 中的元素,那麼它可能有預期外的行為。

功能特性測試 標準 功能特性
__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)

參閱

複製範圍並忽略滿足特定條件的元素
(函數模板) [編輯]
移除範圍中連續重複元素
(函數模板) [編輯]
移除滿足特定條件的元素
(算法函數對象) [編輯]