# std::ranges::remove, std::ranges::remove_if

< cpp‎ | algorithm‎ | ranges

定义于头文件 `<algorithm>` |  |
调用签名 |  |
template< std::permutable I, std::sentinel_for<I> S, class T, class Proj = std::identity > requires std::indirect_binary_predicate< ranges::equal_to, std::projected<I, Proj>, const T*> constexpr ranges::subrange<I> remove( I first, S last, const T& value, Proj proj = {} ); | (1) | (C++20 起)
template< ranges::forward_range R, class T, class Proj = std::identity > requires std::permutable<ranges::iterator_t<R>> && std::indirect_binary_predicate< ranges::equal_to, std::projected<ranges::iterator_t<R>, Proj>, const T*> constexpr ranges::borrowed_subrange_t<R> remove( R&& r, const T& value, Proj proj = {} ); | (2) | (C++20 起)
template< std::permutable I, std::sentinel_for<I> S, class Proj = std::identity, std::indirect_unary_predicate<std::projected<I, Proj>> Pred > constexpr ranges::subrange<I> remove_if( I first, S last, Pred pred, Proj proj = {} ); | (3) | (C++20 起)
template< ranges::forward_range R, class Proj = std::identity, std::indirect_unary_predicate<std::projected<ranges::iterator_t<R>, Proj>> Pred > requires std::permutable<ranges::iterator_t<R>> constexpr ranges::borrowed_subrange_t<R> remove_if( R&& r, Pred pred, Proj proj = {} ); | (4) | (C++20 起)

从范围 `[first, last)` 移除所有满足特定判别标准的元素，并返回子范围 `[ret, last)` ，其中 `ret` 是范围新末尾的尾后迭代器。

1) 移除所有等于 `value` 的元素，用 std::invoke(proj, *i) == value 比较。
3) 移除所有使得 std::invoke(pred, std::invoke(proj, *i)) 返回 true 的元素。
2,4) 同 (1,3) ，但以 `r` 为范围，如同以 ranges::begin(r) 为 `first` 并以 ranges::end(r) 为 `last` 。

通过使未被移除的元素出现在范围起始这种方式，迁移（方式为移动赋值）范围中元素，来进行移除。保持剩余元素的相对顺序而容器的物理大小保持不变。指向新的逻辑结尾与物理结尾之间的迭代器仍然可解引用，但元素自身拥有未指定的值（经由可移动赋值 (MoveAssignable) 的后条件）。

此页面上描述的仿函数实体是 niebloid ，即：

- 调用它们中的任何一者时不可指定显式模板实参。
- 它们无一对实参依赖查找可见。
- 在通常无限定查找寻找函数调用运算符左侧的名称找到它们之一时，它禁止实参依赖查找。

实际上，它们能以函数对象，或以某些特殊编译器扩展实现。

### 参数

first, last | - | 要处理的元素范围
r | - | 要处理的元素范围
value | - | 要移除的元素的值
pred | - | 应用到投影后元素的谓词
proj | - | 应用到元素的投影

### 返回值

{ret, last} ，其中 `[first, ret)` 是移除后作为结果的子范围，而子范围 `[ret, last)` 中的元素在合法但未指定的状态。

### 复杂度

准确应用 `N` 次对应谓词和任何投影，其中 N = ranges::distance(first, last) ，及最坏情况下 N-1 次移动操作。

### 注解

调用 `ranges::remove` 常后随容器的 `erase` 成员函数，它擦除未指定值并减小容器的物理大小以匹配其新的逻辑大小。这两个调用一并被称为擦除-移除手法，它可通过对所有标准序列容器重载的 std::erase 自由函数，或对所有标准容器重载的 std::erase_if 自由函数达成。

名称类似的容器成员函数 `list::remove`、 `list::remove_if`、 `forward_list::remove` 及 `forward_list::remove_if` 擦除被移除的元素。

这些算法通常不能用于如 std::set 与 std::map 的关联容器，因为其迭代器类型不解引用为可移动赋值 (MoveAssignable) 类型（这些容器中的键不可修改）。

因为 `ranges::remove` 按引用接收 `value` ，若它是到范围 `[first, last)` 中元素的引用则函数可能有非期待的行为。

### 可能的实现

版本一
    struct remove_fn {
      template<std::permutable I, std::sentinel_for<I> S, class T, class Proj = std::identity>
        requires std::indirect_binary_predicate<
            ranges::equal_to, std::projected<I, Proj>, const T*>
          constexpr ranges::subrange<I>
            operator()( I first, S last, const T& value, Proj proj = {} ) const {
                first = ranges::find(std::move(first), last, value, proj);
                if (first != last) {
                    for (I i { std::next(first) }; i != last; ++i) {
                        if (value != std::invoke(proj, *i)) {
                            *first = ranges::iter_move(i);
                            ++first;
                        }
                    }
                }
                return {first, last};
            }

      template<ranges::forward_range R, class T, class Proj = std::identity>
        requires std::permutable<ranges::iterator_t<R>> &&
                   std::indirect_binary_predicate<
                     ranges::equal_to, std::projected<ranges::iterator_t<R>, Proj>, const T*>
          constexpr ranges::borrowed_subrange_t<R>
            operator()( R&& r, const T& value, Proj proj = {} ) const {
              return (*this)(ranges::begin(r), ranges::end(r), value, std::move(proj));
            }
    };

    inline constexpr remove_fn remove{};
版本二
    struct remove_if_fn {
      template<std::permutable I, std::sentinel_for<I> S, class Proj = std::identity,
               std::indirect_unary_predicate<std::projected<I, Proj>> Pred>
        constexpr ranges::subrange<I>
          operator()( I first, S last, Pred pred, Proj proj = {} ) const {
              first = ranges::find_if(std::move(first), last, pred, proj);
              if (first != last) {
                  for (I i { std::next(first) }; i != last; ++i) {
                      if (!std::invoke(pred, std::invoke(proj, *i))) {
                          *first = ranges::iter_move(i);
                          ++first;
                      }
                  }
              }
              return {first, last};
          }

      template<ranges::forward_range R, class Proj = std::identity,
               std::indirect_unary_predicate<std::projected<ranges::iterator_t<R>, Proj>> Pred>
        requires std::permutable<ranges::iterator_t<R>>
          constexpr ranges::borrowed_subrange_t<R>
            operator()( R&& r, Pred pred, Proj proj = {} ) const {
              return (*this)(ranges::begin(r), ranges::end(r), pred, std::move(proj));
            }
      };

    inline constexpr remove_if_fn remove_if{};

### 示例

    #include <algorithm>
    #include <cctype>
    #include <iomanip>
    #include <iostream>

    int main()
    {
        std::string ndr{"N o _ D i a g n o s t i c _ R e q u i r e d"};
        std::cout << std::quoted(ndr) << " -> ";
        const auto ret1 = std::ranges::remove(ndr, ' ');
        ndr.erase(ret1.begin(), ret1.end());
        std::cout << std::quoted(ndr) << "\n\n";

        auto rm = [](char c) { return std::islower(c) or std::isspace(c); };
        std::string sfinae{"Substitution Failure Is Not An Error"};
        std::cout << std::quoted(sfinae) << " -> ";
        const auto ret2 = std::ranges::remove_if(sfinae, rm);
        sfinae.erase(ret2.begin(), ret2.end());
        std::cout << std::quoted(sfinae) << '\n';
    }

输出：

    "N o _ D i a g n o s t i c _ R e q u i r e d" -> "No_Diagnostic_Required"

    "Substitution Failure Is Not An Error" -> "SFINAE"

### 参阅

ranges::remove_copy ranges::remove_copy_if (C++20)(C++20) | 复制一个范围的元素，忽略满足特定判别标准的元素 (niebloid)
ranges::unique (C++20) | 移除范围中的连续重复元素 (niebloid)
remove remove_if | 移除满足特定判别标准的元素 (函数模板)