Bootstrap

std::ranges::replace, std::ranges::replace_if,std::ranges::replace_copy

std::ranges::replace

 C++20 引入的算法,用于将范围中所有等于某个特定值的元素替换为另一个值。它是 std::replace 的范围版本,支持范围操作和更现代的 C++ 风格。

功能

  • 遍历范围 r,将所有等于 old_value 的元素替换为 new_value

  • 替换是就地(in-place)进行的,即直接修改输入源范围

Defined in header <algorithm>

Call signature

(1)
template< std::input_iterator I, std::sentinel_for<I> S,

          class T1, class T2, class Proj = std::identity >
requires std::indirectly_writable<I, const T2&> &&
         std::indirect_binary_predicate
             <ranges::equal_to, std::projected<I, Proj>, const T1*>
constexpr I replace( I first, S last, const T1& old_value,

                     const T2& new_value, Proj proj = {} );
(since C++20)
(until C++26)
template< std::input_iterator I, std::sentinel_for<I> S,

          class Proj = std::identity,
          class T1 = std::projected_value_t<I, Proj>, class T2 = T1 >
requires std::indirectly_writable<I, const T2&> &&
         std::indirect_binary_predicate
             <ranges::equal_to, std::projected<I, Proj>, const T1*>
constexpr I replace( I first, S last, const T1& old_value,

                     const T2& new_value, Proj proj = {} );
(since C++26)
(2)
template< ranges::input_range R,

          class T1, class T2, class Proj = std::identity >
requires std::indirectly_writable<ranges::iterator_t<R>, const T2&> &&
         std::indirect_binary_predicate
             <ranges::equal_to,
              std::projected<ranges::iterator_t<R>, Proj>, const T1*>
constexpr ranges::borrowed_iterator_t<R>
    replace( R&& r, const T1& old_value,

             const T2& new_value, Proj proj = {} );
(since C++20)
(until C++26)
template< ranges::input_range R,

          class Proj = std::identity,
          class T1 = std::projected_value_t<ranges::iterator_t<R>, Proj>,
          class T2 = T1 >
requires std::indirectly_writable<ranges::iterator_t<R>, const T2&> &&
         std::indirect_binary_predicate
             <ranges::equal_to,
              std::projected<ranges::iterator_t<R>, Proj>, const T1*>
constexpr ranges::borrowed_iterator_t<R>
    replace( R&& r, const T1& old_value,

             const T2& new_value, Proj proj = {} );
(since C++26
  • 参数:

    • r: 要操作的范围,可以是任何满足 ranges::input_range 的范围(如容器、数组等)。

    • old_value: 需要被替换的值。

    • new_value: 替换后的新值。

  • 返回值: 无(void),直接修改输入范围。


示例 1

#include <iostream>
#include <vector>
#include <algorithm> // std::ranges::replace
#include <ranges>    // C++20 范围库

int main() {
    // 示例 1: 替换 vector 中的元素
    std::vector<int> vec = {1, 2, 3, 2, 4, 2, 5};

    // 将所有值为 2 的元素替换为 99
    std::ranges::replace(vec, 2, 99);

    // 输出结果
    std::cout << "After replace: ";
    for (int i : vec) {
        std::cout << i << " ";
    }
    std::cout << "\n";

    // 示例 2: 替换数组中的元素
    int arr[] = {10, 20, 10, 30, 10, 40};

    // 将所有值为 10 的元素替换为 100
    std::ranges::replace(arr, 10, 100);

    // 输出结果
    std::cout << "After replace: ";
    for (int i : arr) {
        std::cout << i << " ";
    }
    std::cout << "\n";

    return 0;
}

输出:

After replace: 1 99 3 99 4 99 5 
After replace: 100 20 100 30 100 40 
 

详解

  1. 范围支持:

    • std::ranges::replace 支持任何满足 ranges::input_range 的范围,包括标准容器(如 std::vectorstd::list)、原生数组等。

    • 与传统的 std::replace 相比,std::ranges::replace 更符合现代 C++ 的范围操作风格。

  2. 就地修改:

    • std::ranges::replace 直接修改输入范围,而不是返回一个新的范围。

  3. 复杂度:

    • 时间复杂度为 O(N),其中 N 是范围的大小。算法会遍历整个范围一次。

  4. 适用场景:

    • 当需要将范围中所有等于某个值的元素替换为另一个值时使用。

    • 例如,将容器中的所有 0 替换为 -1,或者将所有空字符串替换为默认值。

std::ranges::replace_if

C++20 引入的算法,用于将范围中满足特定条件的元素替换为另一个值。它是 std::replace_if 的范围版本,支持范围操作和更现代的 C++ 风格。

功能

  • 遍历范围 r,对所有满足谓词 pred 的元素替换为 new_value

  • 替换是就地(in-place)进行的,即直接修改输入源范围

template< std::input_iterator I, std::sentinel_for<I> S,

          class T, class Proj = std::identity,
          std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
requires std::indirectly_writable<I, const T&>
constexpr I replace_if( I first, S last, Pred pred,

                        const T& new_value, Proj proj = {} );
(since C++20)
(until C++26)
template< std::input_iterator I, std::sentinel_for<I> S,

          class Proj = std::identity,
          class T = std::projected_value_t<I, Proj>,
          std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
requires std::indirectly_writable<I, const T&>
constexpr I replace_if( I first, S last, Pred pred,

                        const T& new_value, Proj proj = {} );
(since C++26)
(4)
template< ranges::input_range R, class T, class Proj = std::identity,

          std::indirect_unary_predicate<
              std::projected<ranges::iterator_t<R>, Proj>> Pred >
requires std::indirectly_writable<ranges::iterator_t<R>, const T&>
constexpr ranges::borrowed_iterator_t<R>

    replace_if( R&& r, Pred pred, const T& new_value, Proj proj = {} );
(since C++20)
(until C++26)
template< ranges::input_range R, class Proj = std::identity,

          class T = std::projected_value_t<ranges::iterator_t<R>, Proj>,
          std::indirect_unary_predicate<
              std::projected<ranges::iterator_t<R>, Proj>> Pred >
requires std::indirectly_writable<ranges::iterator_t<R>, const T&>
constexpr ranges::borrowed_iterator_t<R>

    replace_if( R&& r, Pred pred, const T& new_value, Proj proj = {} );
(since C++26)
  • 参数:

    • r: 要操作的范围,可以是任何满足 ranges::input_range 的范围(如容器、数组等)。

    • pred: 一元谓词(函数或可调用对象),用于判断是否需要替换元素。如果 pred(e) 返回 true,则元素 e 会被替换。

    • new_value: 替换后的新值。

  • 返回值: 无(void),直接修改输入范围。


示例 1

#include <iostream>
#include <vector>
#include <algorithm> // std::ranges::replace_if
#include <ranges>    // C++20 范围库

int main() {
    // 示例 1: 替换 vector 中所有偶数元素为 -1
    std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    // 将所有偶数替换为 -1
    std::ranges::replace_if(vec, [](int x) { return x % 2 == 0; }, -1);

    // 输出结果
    std::cout << "After replace_if: ";
    for (int i : vec) {
        std::cout << i << " ";
    }
    std::cout << "\n";

    // 示例 2: 替换数组中所有大于 10 的元素为 0
    int arr[] = {5, 12, 8, 15, 3, 20};

    // 将所有大于 10 的元素替换为 0
    std::ranges::replace_if(arr, [](int x) { return x > 10; }, 0);

    // 输出结果
    std::cout << "After replace_if: ";
    for (int i : arr) {
        std::cout << i << " ";
    }
    std::cout << "\n";

    return 0;
}

    输出:

    After replace_if: 1 -1 3 -1 5 -1 7 -1 9 
    After replace_if: 5 0 8 0 3 0 

    详解

    1. 范围支持:

      • std::ranges::replace_if 支持任何满足 ranges::input_range 的范围,包括标准容器(如 std::vectorstd::list)、原生数组等。

      • 与传统的 std::replace_if 相比,std::ranges::replace_if 更符合现代 C++ 的范围操作风格。

    2. 谓词:

      • 谓词 pred 是一个一元函数或可调用对象,接受范围中的元素作为参数,并返回 bool 值。

      • 如果 pred(e) 返回 true,则元素 e 会被替换为 new_value

    3. 就地修改:

      • std::ranges::replace_if 直接修改输入范围,而不是返回一个新的范围。

    4. 复杂度:

      • 时间复杂度为 O(N),其中 N 是范围的大小。算法会遍历整个范围一次。

    5. 适用场景:

      • 当需要根据条件替换范围中的元素时使用。

      • 例如,将所有负数替换为 0,或将所有空字符串替换为默认值。

    std::ranges::replace_copy


     C++20 引入的算法,用于将输入范围内的元素复制到输出范围,同时替换满足条件的值。它是 std::replace_copy 的范围化版本,支持更灵活的迭代器和范围操作。

    功能


    将输入范围 [first, last) 的元素复制到输出范围,同时将所有等于 old_value 的元素替换为 new_value。原始输入范围不会被修改

    Defined in header <algorithm>

    Call signature

    (1)
    template< std::input_iterator I, std::sentinel_for<I> S, class T1, class T2,

              std::output_iterator<const T2&> O, class Proj = std::identity >
    requires std::indirectly_copyable<I, O> &&
             std::indirect_binary_predicate
                 <ranges::equal_to, std::projected<I, Proj>, const T1*>
    constexpr replace_copy_result<I, O>
        replace_copy( I first, S last, O result, const T1& old_value,

                      const T2& new_value, Proj proj = {} );
    (since C++20)
    (until C++26)
    template< std::input_iterator I, std::sentinel_for<I> S,

              class O, class Proj = std::identity,
              class T1 = std::projected_value_t<I, Proj>,
              class T2 = std::iter_value_t<O> >
    requires std::indirectly_copyable<I, O> &&
             std::indirect_binary_predicate
                 <ranges::equal_to, std::projected<I, Proj>, const T1*> &&
             std::output_iterator<O, const T2&>
    constexpr replace_copy_result<I, O>
        replace_copy( I first, S last, O result, const T1& old_value,

                      const T2& new_value, Proj proj = {} );
    (since C++26)
    (2)
    template< ranges::input_range R, class T1, class T2,

              std::output_iterator<const T2&> O, class Proj = std::identity >
    requires std::indirectly_copyable<ranges::iterator_t<R>, O> &&
             std::indirect_binary_predicate
                 <ranges::equal_to,
                  std::projected<ranges::iterator_t<R>, Proj>, const T1*>
    constexpr replace_copy_result<ranges::borrowed_iterator_t<R>, O>
        replace_copy( R&& r, O result, const T1& old_value,

                      const T2& new_value, Proj proj = {} );
    (since C++20)
    (until C++26)
    template< ranges::input_range R,

              class O, class Proj = std::identity,
              class T1 = std::projected_value_t<ranges::iterator_t<R>, Proj>,
              class T2 = std::iter_value_t<O> >
    requires std::indirectly_copyable<ranges::iterator_t<R>, O> &&
             std::indirect_binary_predicate
                 <ranges::equal_to,
                  std::projected<ranges::iterator_t<R>, Proj>, const T1*> &&
             std::output_iterator<O, const T2&>
    constexpr replace_copy_result<ranges::borrowed_iterator_t<R>, O>
        replace_copy( R&& r, O result, const T1& old_value,

                      const T2& new_value, Proj proj = {} );

     

    • 参数:

      • r: 输入范围,可以是任何满足 ranges::input_range 的范围(如容器、数组等)。

      • result: 目标范围的起始迭代器,用于存储复制后的结果。

      • old_value: 需要被替换的值。

      • new_value: 替换后的新值。

    • 返回值:

      {last, result + N}.

      返回 reverse_copy_result,包含两个成员:

    • in:输入范围的结束迭代器。
    • out:输出范围的结束迭代器。

    示例 1:

    #include <iostream>
    #include <vector>
    #include <algorithm> // std::ranges::replace_copy
    #include <ranges>    // C++20 范围库
    
    int main() {
        // 示例 1: 替换 vector 中的元素并复制到另一个 vector
        std::vector<int> src = {1, 2, 3, 2, 4, 2, 5};
        std::vector<int> dst(src.size()); // 目标范围,大小与源范围相同
    
        // 将所有值为 2 的元素替换为 99,并复制到 dst
        auto result = std::ranges::replace_copy(src, dst.begin(), 2, 99);
    
        // 输出结果
        std::cout << "Source after replace_copy: ";
        for (int i : src) {
            std::cout << i << " ";
        }
        std::cout << "\n";
    
        std::cout << "Destination after replace_copy: ";
        for (auto it = dst.begin(); it != result.out; ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n";
    
        // 示例 2: 替换数组中的元素并复制到另一个数组
        int src_arr[] = {10, 20, 10, 30, 10, 40};
        int dst_arr[6]; // 目标数组,大小与源数组相同
    
        // 将所有值为 10 的元素替换为 100,并复制到 dst_arr
        auto end_arr = std::ranges::replace_copy(src_arr, dst_arr, 10, 100);
    
        // 输出结果
        std::cout << "Source array after replace_copy: ";
        for (int i : src_arr) {
            std::cout << i << " ";
        }
        std::cout << "\n";
    
        std::cout << "Destination array after replace_copy: ";
        for (auto it = std::begin(dst_arr); it != end_arr.out; ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n";
    
        return 0;
    }

    输出:

    Source after replace_copy: 1 2 3 2 4 2 5 
    Destination after replace_copy: 1 99 3 99 4 99 5 
    Source array after replace_copy: 10 20 10 30 10 40 
    Destination array after replace_copy: 100 20 100 30 100 40 

    详解

    1. 范围支持:

      • std::ranges::replace_copy 支持任何满足 ranges::input_range 的输入范围,以及任何满足 ranges::output_iterator 的目标迭代器。

      • 与传统的 std::replace_copy 相比,std::ranges::replace_copy 更符合现代 C++ 的范围操作风格。

    2. 不修改输入范围:

      • std::ranges::replace_copy 不会修改输入范围,而是将结果复制到目标范围。

    3. 返回值:

      • 返回目标范围的结束迭代器,可以用于确定复制后的有效范围。

    4. 复杂度:

      • 时间复杂度为 O(N),其中 N 是输入范围的大小。算法会遍历整个输入范围一次。

    5. 适用场景:

      • 当需要复制一个范围并替换其中的某些元素时使用。

      • 例如,将容器中的所有 0 替换为 -1,并将结果存储到另一个容器中。

    关键点


    不修改原范围:输入范围保持不变,结果写入输出范围。
    输出迭代器:需确保输出范围有足够空间(或使用 back_inserter 动态扩展)。
    复杂度:线性复杂度 O(N),N 为输入范围大小。
    迭代器要求:输入范围需要是双向迭代器(支持反向遍历)。


    对比 reverse 和 reverse_copy


    | 特性               | std::ranges::reverse | std::ranges::reverse_copy |

     是否修改原范围                 是                             否 

     输出目标                  无(原地修改) |                指定输出范围

     典型用例              需要原地逆序的场景         需要保留原始数据的逆序副本的场景 

    通过 std::ranges::reverse_copy,可以方便地生成逆序副本,适用于需要保留原始数据顺序的场景。

    返回值详解


    in

    类型为输入迭代器 I,指向输入范围的末尾(即 last)。它表示算法处理完成的输入位置,通常等于输入的结束迭代器。

    out


    类型为输出迭代器 O,指向输出范围中最后一个被复制元素的下一个位置。可以通过 out 迭代器继续向输出范围写入其他数据。

    示例 2

    #include <algorithm>
    #include <vector>
    #include <iostream>
    #include <iterator>
     
    int main()
    {
        std::vector<int> src = {1, 2, 3, 4, 5};
        std::vector<int> dst;
        dst.reserve(src.size() + 1);
        dst[src.size()] = 10;
     
        // 调用 reverse_copy 并获取返回值
        auto result = std::ranges::reverse_copy(src, dst.begin());
     
        // 输出输入范围和输出范围的信息
        std::cout << "输入范围是否处理完毕?" 
                  << (result.in == src.end() ? "是" : "否") 
                  << std::endl; // 输出:是
     
        std::cout << "输出范围的大小: " << dst.size() << std::endl; // 输出:5
     
        //这里由于dst多一个空间,out指向了dst[5]的位置,即复制完元素的下一个位置。
        std::cout << "输出结束迭代器指向的位置: " << *result.out << std::endl; // 输出:10
    }

    std::ranges::replace_copy_if

           C++20 引入的算法,用于将范围中的元素复制到目标范围,并在复制过程中将所有满足特定条件的元素替换为另一个值。它是 std::replace_copy_if 的范围版本,支持范围操作和更现代的 C++ 风格。

    功能

    • 遍历输入范围 r,将所有满足谓词 pred 的元素替换为 new_value,并将结果复制到目标范围。

    • 输入范围不会被修改,结果存储在目标范围中。

    template< std::input_iterator I, std::sentinel_for<I> S,

              class T, std::output_iterator<const T&> O,
              class Proj = std::identity,
              std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
    requires std::indirectly_copyable<I, O>
    constexpr replace_copy_if_result<I, O>
        replace_copy_if( I first, S last, O result, Pred pred,

                         const T& new_value, Proj proj = {} );
    (since C++20)
    (until C++26)
    template< std::input_iterator I, std::sentinel_for<I> S,

              class O, class T = std::iter_value_t<O>
              class Proj = std::identity,
              std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
    requires std::indirectly_copyable<I, O> && std::output_iterator<O, const T&>
    constexpr replace_copy_if_result<I, O>
        replace_copy_if( I first, S last, O result, Pred pred,

                         const T& new_value, Proj proj = {} );
    (since C++26)
    (4)
    template< ranges::input_range R,

              class T, std::output_iterator<const T&> O,
              class Proj = std::identity,
              std::indirect_unary_predicate
                  <std::projected<ranges::iterator_t<R>, Proj>> Pred >
    requires std::indirectly_copyable<ranges::iterator_t<R>, O>
    constexpr replace_copy_if_result<ranges::borrowed_iterator_t<R>, O>
        replace_copy_if( R&& r, O result, Pred pred,

                         const T& new_value, Proj proj = {} );
    (since C++20)
    (until C++26)
    template< ranges::input_range R,

              class O, class T = std::iter_value_t<O>
              class Proj = std::identity,
              std::indirect_unary_predicate
                  <std::projected<ranges::iterator_t<R>, Proj>> Pred >
    requires std::indirectly_copyable<ranges::iterator_t<R>, O> &&
             std::output_iterator<O, const T&>
    constexpr replace_copy_if_result<ranges::borrowed_iterator_t<R>, O>
        replace_copy_if( R&& r, O result, Pred pred,

                         const T& new_value, Proj proj = {} );
    (since C++26)
    • 参数:

      • r: 输入范围,可以是任何满足 ranges::input_range 的范围(如容器、数组等)。

      • result: 目标范围的起始迭代器,用于存储复制后的结果。

      • pred: 一元谓词(函数或可调用对象),用于判断是否需要替换元素。如果 pred(e) 返回 true,则元素 e 会被替换为 new_value

      • new_value: 替换后的新值。

    • 返回值: 返回目标范围的结束迭代器(即最后一个被复制元素的下一个位置)。


    示例:

    #include <iostream>
    #include <vector>
    #include <algorithm> // std::ranges::replace_copy_if
    #include <ranges>    // C++20 范围库
    
    int main() {
        // 示例 1: 替换 vector 中所有偶数元素为 -1,并复制到另一个 vector
        std::vector<int> src = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        std::vector<int> dst(src.size()); // 目标范围,大小与源范围相同
    
        // 将所有偶数替换为 -1,并复制到 dst
        auto end = std::ranges::replace_copy_if(
            src, dst.begin(), [](int x) { return x % 2 == 0; }, -1);
    
        // 输出结果
        std::cout << "Source after replace_copy_if: ";
        for (int i : src) {
            std::cout << i << " ";
        }
        std::cout << "\n";
    
        std::cout << "Destination after replace_copy_if: ";
        for (auto it = dst.begin(); it != end.out; ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n";
    
        // 示例 2: 替换数组中所有大于 10 的元素为 0,并复制到另一个数组
        int src_arr[] = {5, 12, 8, 15, 3, 20};
        int dst_arr[6]; // 目标数组,大小与源数组相同
    
        // 将所有大于 10 的元素替换为 0,并复制到 dst_arr
        auto end_arr = std::ranges::replace_copy_if(
            src_arr, dst_arr, [](int x) { return x > 10; }, 0);
    
        // 输出结果
        std::cout << "Source array after replace_copy_if: ";
        for (int i : src_arr) {
            std::cout << i << " ";
        }
        std::cout << "\n";
    
        std::cout << "Destination array after replace_copy_if: ";
        for (auto it = std::begin(dst_arr); it != end_arr.out; ++it) {
            std::cout << *it << " ";
        }
        std::cout << "\n";
    
        return 0;
    }

    输出:

    Source after replace_copy_if: 1 2 3 4 5 6 7 8 9 
    Destination after replace_copy_if: 1 -1 3 -1 5 -1 7 -1 9 
    Source array after replace_copy_if: 5 12 8 15 3 20 
    Destination array after replace_copy_if: 5 0 8 0 3 0 

    ;