Bootstrap

std::ranges::copy, copy_if, copy_n, copy_backward, replace_copy, replace_copy_if, reverse_copy

std::ranges::copy

C++20 引入的范围库(Ranges Library)中的算法,用于将输入范围(如容器、数组或视图)的元素复制到指定的输出位置。它是对传统 std::copy 的增强,支持更简洁的语法和范围操作。

基本功能

  • 作用:将源范围的元素复制到目标范围。
  • 返回值:包含复制结束位置的迭代器(即源范围的结束迭代器和目标范围的结束迭代器)。
  • 要求
    • 源范围和目标范围必须有效。
    • 目标范围必须有足够的空间容纳源范围的元素。

Defined in header <algorithm>

Call signature

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

requires std::indirectly_copyable<I, O>
constexpr copy_result<I, O>

    copy( I first, S last, O result );
(1)(since C++20)
template< ranges::input_range R, std::weakly_incrementable O >

requires std::indirectly_copyable<ranges::iterator_t<R>, O>
constexpr copy_result<ranges::borrowed_iterator_t<R>, O>

    copy( R&& r, O result );
  • 参数
    • first, last:输入范围的迭代器对。
    • r:输入范围(如容器、视图)。
    • result:输出位置的起始迭代器。
  • 返回值ranges::copy_result 结构体,包含输入范围的结束迭代器和输出范围的结束迭代器。

基本用法

示例 1:复制容器内容
#include <algorithm>
#include <vector>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // 使用 back_inserter 动态扩展目标容器
    std::ranges::copy(src, std::back_inserter(dest));

    // dest 内容:{1, 2, 3, 4, 5}
}

 示例 2:输出到流

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};

    // 输出到标准输出,用空格分隔
    std::ranges::copy(src, std::ostream_iterator<int>(std::cout, " "));
    // 输出:1 2 3 4 5
}

高级用法

示例 3:结合范围适配器
#include <algorithm>
#include <vector>
#include <ranges>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // 复制所有偶数
    auto even_view = src | std::views::filter([](int x) { return x % 2 == 0; });
    std::ranges::copy(even_view, std::back_inserter(dest));

    // dest 内容:{2, 4}
}
示例 4:使用投影(间接转换)
#include <algorithm>
#include <vector>
#include <ranges>
#include <iterator>

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}};
    std::vector<int> ages;

    // 投影:提取 Person 的 age 成员
    auto age_view = people | std::views::transform(&Person::age);
    std::ranges::copy(age_view, std::back_inserter(ages));

    // ages 内容:{30, 25, 35}
}

示例 5:返回值详解

  1. .in
    表示源范围的结束迭代器(即复制结束后的位置)。
    对于完整范围复制,.in 等于源范围的 end() 迭代器。

  2. .out
    表示目标范围的结束迭代器(即最后一个被复制元素的下一个位置)。
    可用于继续写入或检查目标范围的空间。

示例

示例 1:获取复制后的目标范围结束位置
#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst(5); // 预先分配 5 个空间

    // 复制并获取返回值
    auto result = std::ranges::copy(src, dst.begin());

    // 输出目标容器的结束位置
    std::cout << "目标范围结束迭代器位置: " << (result.out - dst.begin()) << std::endl;
    // 输出:目标范围结束迭代器位置: 5
}

示例 2:处理不完整复制(目标空间不足)

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst(3); // 只分配 3 个空间

    auto result = std::ranges::copy(src, dst.begin());

    // 检查实际复制的元素数量
    size_t copied_count = result.in - src.begin();
    std::cout << "实际复制的元素数量: " << copied_count << std::endl; // 输出:3

    // 目标容器的结束位置
    std::cout << "目标结束迭代器位置: " << (result.out - dst.begin()) << std::endl; // 输出:3
}

输出:

实际复制的元素数量: 5
目标结束迭代器位置: 5

返回值用途

  1. 检查复制完整性
    通过比较 .in 和源范围的 end(),可以判断是否全部复制成功。

    if (result.in == src.end()) 
    { 
        std::cout << "全部元素已复制" << std::endl; 
    }
  2. 继续写入目标范围
    使用 .out 可以继续向目标范围追加数据:

    std::vector<int> additional = {6, 7, 8}; 
    std::ranges::copy(additional, result.out);


总结

  • .in 和 .out 分别表示源范围和目标范围的结束位置。
  • 返回值可用于验证复制完整性、处理部分复制或继续操作目标范围。

与传统 std::copy 的区别

特性std::copystd::ranges::copy
参数迭代器对 (first, last)直接接受范围 r
返回值输出结束迭代器copy_result 结构体
范围适配器支持需手动组合迭代器支持管道操作符 `

与 std::ranges::copy_backward的区别

      std::ranges::copy        std::ranges::copy_backward

复制方向        从前向后                                  从后向前

迭代器类型    输入迭代器                               双向迭代器

重叠处理    目标起始位置在源前面时安全     目标结束(dst.end())位置在源后面时安全

                                                                       或者说目前范围最后一个复制元素的位置

                                                                       在原范围之内的话是安全的。

注意事项

  1. 目标空间:确保输出迭代器指向的位置有足够的空间,或使用 std::back_inserter 动态扩展容器。
  2. 性能:范围适配器(如 filtertransform)是惰性求值的,不会产生额外开销。
  3. 兼容性:需要编译器支持 C++20 标准(如 GCC 10+、Clang 13+)。

通过 std::ranges::copy,可以更简洁、安全地实现元素复制操作,尤其在与范围适配器结合时,能显著提升代码可读性和灵活性。

std::ranges::copy_if

C++20 范围库中提供的算法,用于将输入范围内满足特定条件的元素复制到输出位置。它是对传统 std::copy_if 的增强,支持更简洁的语法和范围操作。

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

          class Proj = std::identity,
          std::indirect_unary_predicate<std::projected<I, Proj>> Pred >
requires std::indirectly_copyable<I, O>
constexpr copy_if_result<I, O>

    copy_if( I first, S last, O result, Pred pred, Proj proj = {} );
(3)(since C++20)
template< ranges::input_range R, std::weakly_incrementable 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 copy_if_result<ranges::borrowed_iterator_t<R>, O>

    copy_if( R&& r, O result, Pred pred, Proj proj = {} );
  • 参数
    • first, last 或 r:输入范围的迭代器对或范围对象。
    • result:输出位置的起始迭代器。
    • pred:谓词函数,用于筛选元素(返回 true 的元素会被复制)。
    • proj(可选):投影函数,对元素进行转换后再应用谓词。
  • 返回值ranges::copy_if_result 结构体,包含输入范围的结束迭代器和输出范围的结束迭代器。

基本用法

示例 1:复制满足条件的元素
#include <algorithm>
#include <vector>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // 复制所有偶数到 dest
    std::ranges::copy_if(src, std::back_inserter(dest), [](int x) {
        return x % 2 == 0;
    });

    // dest 内容:{2, 4}
}
示例 2:输出到流
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};

    // 输出所有大于 3 的元素
    std::ranges::copy_if(src, std::ostream_iterator<int>(std::cout, " "),
        [](int x) { return x > 3; });

    // 输出:4 5
}

高级用法

示例 3:结合投影(Projection)
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>

struct Person 
{
    std::string name;
    int age;
};

int main() {
    std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}};
    std::vector<Person> names;

    // 复制年龄大于 28 的人的名字
    std::ranges::copy_if(people, std::back_inserter(names),
        [](int age) { return age > 28; },  // 谓词作用于投影后的值
        &Person::age                        // 投影:提取 Person::age
    );

    // names 内容:{"Alice", "Charlie"}
    for(const auto&[name, _]: names)
    {
        std::cout <<name << std::endl;
    }
}
示例 4:与范围适配器结合
#include <algorithm>
#include <vector>
#include <ranges>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // 使用视图过滤后再复制(等效于 copy_if)
    auto even_view = src | std::views::filter([](int x) { return x % 2 == 0; });
    std::ranges::copy(even_view, std::back_inserter(dest));

    // dest 内容:{2, 4}
}

std::ranges::copy_if 

是 C++20 范围库中提供的算法,用于将输入范围内满足特定条件的元素复制到输出位置。它是对传统 std::copy_if 的增强,支持更简洁的语法和范围操作。

功能

  • 遍历输入范围 [first, last)
  • 对每个元素应用投影 proj,再通过谓词 pred 判断。
  • 若条件为 true,则复制到 result 指向的目标位置。
  • 返回最终输入和输出迭代器的位置。

函数原型

#include <algorithm>  // 需要包含的头文件
#include <iterator>   // 输出迭代器相关

template<
    std::input_iterator I,
    std::sentinel_for<I> S,
    std::weakly_incrementable O,
    typename Proj = std::identity,
    std::indirect_unary_predicate<std::projected<I, Proj>> Pred
>
constexpr ranges::copy_if_result<I, O>
    copy_if(I first, S last, O result, Pred pred, Proj proj = {});

template<
    std::ranges::input_range R,
    std::weakly_incrementable O,
    typename Proj = std::identity,
    std::indirect_unary_predicate<std::projected<std::ranges::iterator_t<R>, Proj>> Pred
>
constexpr ranges::copy_if_result<std::ranges::borrowed_iterator_t<R>, O>
    copy_if(R&& r, O result, Pred pred, Proj proj = {});
  • 参数
    • first, last 或 r:输入范围的迭代器对或范围对象。
    • result:输出位置的起始迭代器。
    • pred:谓词函数,用于筛选元素(返回 true 的元素会被复制)。
    • proj(可选):投影函数,对元素进行转换后再应用谓词。
  • 返回值ranges::copy_if_result 结构体,包含输入范围的结束迭代器和输出范围的结束迭代器。

基本用法

示例 1:复制满足条件的元素
#include <algorithm>
#include <vector>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // 复制所有偶数到 dest
    std::ranges::copy_if(src, std::back_inserter(dest), [](int x) {
        return x % 2 == 0;
    });

    // dest 内容:{2, 4}
}
示例 2:输出到流
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};

    // 输出所有大于 3 的元素
    std::ranges::copy_if(src, std::ostream_iterator<int>(std::cout, " "),
        [](int x) { return x > 3; });

    // 输出:4 5
}

高级用法

示例 3:结合投影(Projection)
#include <algorithm>
#include <vector>
#include <string>

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}};
    std::vector<std::string> names;

    // 复制年龄大于 28 的人的名字
    std::ranges::copy_if(people, std::back_inserter(names),
        [](int age) { return age > 28; },  // 谓词作用于投影后的值
        &Person::age                        // 投影:提取 Person::age
    );

    // names 内容:{"Alice", "Charlie"}
}
示例 4:与范围适配器结合
#include <algorithm>
#include <vector>
#include <ranges>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // 使用视图过滤后再复制(等效于 copy_if)
    auto even_view = src | std::views::filter([](int x) { return x % 2 == 0; });
    std::ranges::copy(even_view, std::back_inserter(dest));

    // dest 内容:{2, 4}
}

与传统 std::copy_if 的区别

特性std::copy_ifstd::ranges::copy_if
参数迭代器对 (first, last)直接接受范围 r
投影支持支持 proj 参数
返回值输出结束迭代器copy_if_result 结构体
范围适配器集成需手动组合迭代器支持管道操作符 `

注意事项

  1. 输出空间:确保输出迭代器指向的位置有足够空间,或使用 std::back_inserter 动态扩展容器。
  2. 谓词与投影
    • 谓词应接受投影后的值类型(若使用投影)。
    • 避免在谓词或投影中修改元素(除非明确需要副作用)。
  3. 兼容性:需要编译器支持 C++20(如 GCC 10+、Clang 13+)。

总结

std::ranges::copy_if 通过以下特性提升代码质量:

  • 简洁性:直接操作范围,无需手动处理迭代器。
  • 灵活性:支持投影和范围适配器,便于组合复杂操作。
  • 安全性:返回值明确标识输入和输出的结束位置。

std::ranges::copy_n

 C++20 引入的范围库算法,用于从输入范围复制指定数量的元素到目标位置。它是传统 std::copy_n 的范围化版本,提供更简洁的语法和更强的类型安全性。

Defined in header <algorithm>

Call signature

template< std::input_iterator I, std::weakly_incrementable O >

requires std::indirectly_copyable<I, O>
constexpr copy_n_result<I, O>

    copy_n( I first, std::iter_difference_t<I> n, O result );
(1)(since C++20)

Helper type

template< class I, class O >
using copy_n_result = ranges::in_out_result<I, O>;

  • 参数

    • first:输入范围的起始迭代器。
    • n:要复制的元素数量。
    • result:目标位置的输出迭代器。
  • 返回值

    • ranges::copy_n_result<I, O>:包含输入迭代器和输出迭代器的结构,表示复制结束后的位置。

功能

  • 从输入迭代器 first 开始,复制 n 个连续元素到目标位置 result
  • 返回的迭代器对中:
    • .in 指向输入范围中最后一个被复制的元素的下一个位置。
    • .out 指向目标范围中最后一个写入元素的下一个位置。

示例

示例 1:复制到动态容器
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst;

    // 复制前 3 个元素到 dst(使用 back_inserter 动态扩展)
    std::ranges::copy_n(src.begin(), 3, std::back_inserter(dst));

    // 输出 dst: 1 2 3
    for (int x : dst) {
        std::cout << x << " ";
    }
}
示例 2:复制到固定数组
#include <algorithm>
#include <iostream>

int main() {
    int src[] = {10, 20, 30, 40};
    int dst[4] = {0}; // 初始化 dst 为 {0, 0, 0, 0}

    // 复制前 3 个元素到 dst 的起始位置
    std::ranges::copy_n(src, 3, dst);

    // 输出 dst: 10 20 30 0
    for (int x : dst) {
        std::cout << x << " ";
    }
}

示例 3

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
#include <string_view>
 
int main()
{
    const std::string_view in {"ABCDEFGH"};
    std::string out;
 
    std::ranges::copy_n(in.begin(), 4, std::back_inserter(out));
    std::cout << std::quoted(out) << '\n';
 
    out = "abcdefgh";
    const auto res = std::ranges::copy_n(in.begin(), 5, out.begin());
    std::cout
        << "*(res.in): '" << *(res.in) << "', distance: "
        << std::distance(std::begin(in), res.in) << '\n'
        << "*(res.out): '" << *(res.out) << "', distance: "
        << std::distance(std::begin(out), res.out) << '\n';
}

Output:

"ABCD"
*(res.in): 'F', distance: 5
*(res.out): 'f', distance: 5

注意事项

  1. 输入范围有效性必须确保输入范围至少有 n 个元素,否则行为未定义
  2. 目标空间目标位置必须有足够空间容纳 n 个元素,或使用 std::back_inserter 动态扩展容器。
  3. 迭代器类型
    • 输入迭代器 I 需满足 std::input_iterator
    • 输出迭代器 O 需满足 std::weakly_incrementable(允许写入操作)。

应用场景

  • 提取固定长度的数据片段(如读取缓冲区的前 N 字节)。
  • 复制容器的部分内容到另一个容器。
  • 初始化目标范围的前 N 个元素。

通过 std::ranges::copy_n,可以高效且安全地实现元素的批量复制操作。

std::ranges::copy_backward

 C++20 引入的算法,用于将范围中的元素从后向前复制到目标位置(目标范围也是从后向前赋值)。它适用于需要避免覆盖源范围中未复制元素的情况(例如目标范围与源范围有重叠且目标起始位置在源范围中间。

Defined in header <algorithm>

Call signature

template< std::bidirectional_iterator I1, std::sentinel_for<I1> S1,

          std::bidirectional_iterator I2 >
requires std::indirectly_copyable<I1, I2>
constexpr copy_backward_result<I1, I2>

    copy_backward( I1 first, S1 last, I2 d_last );
(1)(since C++20)
template< ranges::bidirectional_range R, std::bidirectional_iterator I >

requires std::indirectly_copyable<ranges::iterator_t<R>, I>
constexpr copy_backward_result<ranges::borrowed_iterator_t<R>, I>

    copy_backward( R&& r, I d_last );
  • 参数

    • firstlast:输入范围的起始和结束迭代器(左闭右开区间 [first, last))。
    • d_last:目标范围的结束迭代器(复制后的元素将终止于此位置,元素从后向前填充)。
  • 返回值:{last, d_last - N}

    • ranges::copy_backward_result<I1, I2>:包含输入范围和目标范围的结束迭代器的结构。

功能

  • 从输入范围的最后一个元素开始,向前遍历并复制到目标范围的末尾。
  • 目标范围的有效区间为 [result_end - N, result_end),其中 N = last - first
  • 关键特性:当源范围和目标范围有重叠时,如果目标起始位置在源范围中间,使用 copy_backward 可以避免覆盖未复制的元素。

示例

示例 1:复制到不重叠范围
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst(5); // 初始化大小为 5 的容器

    // 将 src 的所有元素复制到 dst 的末尾
    std::ranges::copy_backward(src, dst.end());

    // 输出 dst: 1 2 3 4 5
    for (int x : dst) {
        std::cout << x << " ";
    }
}
示例 2:处理重叠范围

当目标范围结束是的位置与在原范围之内(即与源范围重叠时),copy_backward 可以安全复制:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5, 6};

    // 将前 3 个元素复制到从第 4 个位置开始的位置
    std::ranges::copy_backward(
        data.begin(), 
        data.begin() + 3, // 源范围 [1, 2, 3]
        data.end()        // 目标结束位置(第 6 个元素之后)
    );

    // 输出 data: 1 2 3 1 2 3
    for (int x : data) {
        std::cout << x << " ";
    }
}

注意事项

  1. 迭代器方向:输入和目标的迭代器必须是双向迭代器(支持 -- 操作)。
  2. 目标空间:目标范围必须有足够的空间容纳 last - first 个元素。
  3. 重叠处理:当源范围和目标范围重叠时:
    • 如果目标起始位置在源范围的前面,使用 std::ranges::copy。(left)
    • 如果目标起始位置在源范围的后面,使用 std::ranges::copy_backward。(right)

与 std::ranges::copy 的区别

      std::ranges::copy        std::ranges::copy_backward

复制方向        从前向后                                  从后向前

迭代器类型    输入迭代器                               双向迭代器

重叠处理    目标起始位置在源前面时安全     目标结束位置在源范围内时安全

copy:
constexpr ranges::copy_result<I, O> operator()(I first, S last, O result) const
    {
        for (; first != last; ++first, (void)++result)
            *result = *first;
        return {std::move(first), std::move(result)};
    }

copy_backward:
constexpr ranges::copy_backward_result<I1, I2>
        operator()(I1 first, S1 last, I2 d_last) const
    {
        I1 last1 {ranges::next(first, std::move(last))};
        for (I1 i {last1}; i != first;)
            *--d_last = *--i;
        return {std::move(last1), std::move(d_last)};
    }

返回值类型

返回一个 std::ranges::copy_backward_result 结构体,包含两个迭代器:

  1. in: 指向源范围中最后一个被复制的元素的下一个位置(即源范围的结束迭代器)。
  2. out: 指向目标范围中最后一个被复制的元素(即目标范围的起始位置)。
示例 3:
#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest(7, 0); // 初始化为 {0, 0, 0, 0, 0, 0, 0}

    // 将 src 的内容复制到 dest 的第 3 到第 7 个位置(从后向前填充)
    auto result = std::ranges::copy_backward(src, dest.end());

    // 输出目标容器内容
    for (int num : dest) {
        std::cout << num << " ";
    }
    // 输出:0 0 1 2 3 4 5

    // 检查返回值
    std::cout << "\n源范围结束位置是否等于 src.end(): " 
              << (result.in == src.end()); // 输出 1(true)
    
    std::cout << "\n目标起始位置是否等于 dest.begin() + 2: " 
              << (result.out == dest.begin() + 2); // 输出 1(true)
}

适用场景

  • 当需要从后向前填充目标范围时(例如目标范围的末尾已知)。
  • 处理重叠范围时,确保数据完整性。

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 = {} );

参数说明

  • firstlast:输入范围的迭代器对。
  • r:输入范围(可直接传递容器或视图)。
  • result:输出迭代器,指向目标范围的起始位置。
  • old_value:需要被替换的值。
  • new_value:替换后的新值。

返回值

返回 replace_copy_result,包含两个成员:

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

示例 1

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 2, 5};
    std::vector<int> dst;

    // 将 src 中的 2 替换为 99,结果存入 dst
    std::ranges::replace_copy(src, std::back_inserter(dst), 2, 99);

    // 输出结果
    for (int x : dst) {
        std::cout << x << " ";
    }
    // 输出:1 99 3 99 5
}

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

函数原型

 

template < std::input_iterator I, std::sentinel_for<I> S, class T1, class T2, std::output_iterator<const T2&> O > requires std::indirectly_copyable<I, O> && std::indirect_binary_predicate<std::ranges::equal_to, std::indirect_result_t<I>, const T1*> constexpr replace_copy_result<I, O> std::ranges::replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value);

或通过范围接口:

 

template < std::ranges::input_range R, class T1, class T2, std::output_iterator<const T2&> O > requires std::indirectly_copyable<std::ranges::iterator_t<R>, O> && std::indirect_binary_predicate<std::ranges::equal_to, std::ranges::iterator_t<R>, const T1*> constexpr replace_copy_result<std::ranges::borrowed_iterator_t<R>, O> std::ranges::replace_copy(R&& r, O result, const T1& old_value, const T2& new_value);

参数说明

  • firstlast:输入范围的迭代器对。
  • r:输入范围(可直接传递容器或视图)。
  • result:输出迭代器,指向目标范围的起始位置。
  • old_value:需要被替换的值。
  • new_value:替换后的新值。

返回值

返回 replace_copy_result,包含两个成员:

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

功能

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


示例

 

#include <algorithm> #include <vector> #include <iostream> #include <iterator> int main() { std::vector<int> src = {1, 2, 3, 2, 5}; std::vector<int> dst; // 将 src 中的 2 替换为 99,结果存入 dst std::ranges::replace_copy(src, std::back_inserter(dst), 2, 99); // 输出结果 for (int x : dst) { std::cout << x << " "; } // 输出:1 99 3 99 5 }

解释

  1. 输入范围src 是 {1, 2, 3, 2, 5}
  2. 替换规则:将所有值为 2 的元素替换为 99
  3. 输出范围:通过 std::back_inserter(dst) 将结果插入到 dst 容器。
  4. 结果dst 最终为 {1, 99, 3, 99, 5}

进阶示例:自定义类型替换

若需替换自定义类型的对象,需确保类型支持 == 运算符

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

struct Point {
    int x, y;
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
};

int main() {
    std::vector<Point> src = {{1,2}, {3,4}, {1,2}};
    std::vector<Point> dst;

    const Point old_val{1, 2};
    const Point new_val{99, 100};

    std::ranges::replace_copy(src, std::back_inserter(dst), old_val, new_val);

    // 输出结果
    for (const auto& p : dst) {
        std::cout << "(" << p.x << ", " << p.y << ") ";
    }
    // 输出:(99, 100) (3, 4) (99, 100)
}

关键点

  • 不修改原范围replace_copy 不会改变输入范围的内容。
  • 输出迭代器:需确保输出范围有足够空间(或使用 back_inserter 动态扩展)。
  • 复杂度:线性复杂度 O(N),N 为输入范围大小。

通过 std::ranges::replace_copy,可以高效实现“复制并替换”的操作,尤其适合需要保留原始数据的场景。

std::ranges::replace_copy_if

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

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 = {} );

参数说明

  • firstlast:输入范围的迭代器对。
  • r:输入范围(可直接传递容器或视图)。
  • result:输出迭代器,指向目标范围的起始位置。
  • pred:谓词函数,判断元素是否需要被替换。
  • new_value:替换后的新值。
  • proj:投影函数(可选),用于对元素进行转换后再应用谓词。

返回值

返回 replace_copy_if_result,包含两个成员:

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

功能

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


示例 1:替换所有偶数

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst;

    // 将 src 中的偶数替换为 0,结果存入 dst
    auto is_even = [](int x) { return x % 2 == 0; };
    std::ranges::replace_copy_if(src, std::back_inserter(dst), is_even, 0);

    // 输出结果
    for (int x : dst) {
        std::cout << x << " ";
    }
    // 输出:1 0 3 0 5
}

示例 2:自定义类型和投影函数

替换所有 Point 对象中 x 坐标为负数的元素:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

struct Point {
    int x, y;
};

int main() {
    std::vector<Point> src = {{1, 2}, {-3, 4}, {5, -6}};
    std::vector<Point> dst;

    // 谓词:检查投影后的 x 坐标是否为负数
    auto is_negative = [](int x) { return x < 0; };

    // 投影函数:提取 Point 的 x 坐标
    auto proj = [](const Point& p) { return p.x; };

    // 替换所有 x < 0 的 Point 为 {0, 0}
    std::ranges::replace_copy_if(
        src, 
        std::back_inserter(dst), 
        is_negative, 
        Point{0, 0}, 
        proj
    );

    // 输出结果
    for (const auto& p : dst) {
        std::cout << "(" << p.x << ", " << p.y << ") ";
    }
    // 输出:(1, 2) (0, 0) (5, -6)
}

示例 3:使用成员函数作为谓词

替换所有空字符串为 "N/A"

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>
#include <string>

int main() {
    std::vector<std::string> src = {"Hello", "", "World", "", "!"};
    std::vector<std::string> dst;

    // 使用成员函数 std::string::empty 作为谓词
    std::ranges::replace_copy_if(
        src, 
        std::back_inserter(dst), 
        &std::string::empty, 
        "N/A"
    );

    // 输出结果
    for (const auto& s : dst) {
        std::cout << s << " ";
    }
    // 输出:Hello N/A World N/A !
}

关键点

  1. 灵活的条件判断:通过谓词 pred 可以定义任意复杂的替换条件。
  2. 投影函数proj 允许先对元素进行转换(如提取成员变量),再应用谓词。
  3. 不修改原范围:输入范围保持不变,结果写入输出范围。
  4. 复杂度:线性复杂度 O(N),N 为输入范围大小。

std::ranges::reverse_copy

C++20 引入的算法,用于将输入范围内的元素逆序复制到输出范围。它是 std::reverse_copy 的范围化版本,支持更灵活的迭代器和范围操作。不修改原范围。

功能

将输入范围 [first, last) 的元素逆序复制到输出范围,原始输入范围保持不变。

Defined in header <algorithm>

Call signature

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

          std::weakly_incrementable O >
requires std::indirectly_copyable<I, O>
constexpr reverse_copy_result<I, O>

    reverse_copy( I first, S last, O result );
(1)(since C++20)
template< ranges::bidirectional_range R, std::weakly_incrementable O >

requires std::indirectly_copyable<ranges::iterator_t<R>, O>
constexpr reverse_copy_result<ranges::borrowed_iterator_t<R>, O>

    reverse_copy( R&& r, O result );
(2)(since C++20)

Helper types

template< class I, class O >
using reverse_copy_result = ranges::in_out_result<I, O>;

参数说明

  • firstlast:输入范围的迭代器对(需满足双向迭代器)。
  • r:输入范围(可直接传递容器或视图)。
  • result:输出迭代器,指向目标范围的起始位置。

返回值

{last, result + N}.

返回 reverse_copy_result,包含两个成员:

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

示例 1:基本用法

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst;

    // 将 src 逆序复制到 dst
    std::ranges::reverse_copy(src, std::back_inserter(dst));

    // 输出结果
    for (int x : dst) {
        std::cout << x << " ";
    }
    // 输出:5 4 3 2 1
}

示例 2:输出到流

将逆序结果直接输出到标准输出:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};

    // 逆序复制到输出流
    std::ranges::reverse_copy(
        src, 
        std::ostream_iterator<int>(std::cout, " ")
    );

    // 输出:5 4 3 2 1
}

示例 3:自定义类型

逆序复制自定义类型的元素:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

struct Point {
    int x, y;
    friend std::ostream& operator<<(std::ostream& os, const Point& p) {
        return os << "(" << p.x << ", " << p.y << ")";
    }
};

int main() {
    std::vector<Point> src = {{1, 2}, {3, 4}, {5, 6}};
    std::vector<Point> dst;

    std::ranges::reverse_copy(src, std::back_inserter(dst));

    for (const auto& p : dst) {
        std::cout << p << " ";
    }
    // 输出:(5, 6) (3, 4) (1, 2)
}

关键点

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

对比 reverse 和 reverse_copy

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

 是否修改原范围                 是                             否 

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

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


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

返回值详解

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

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


示例 4

以下示例演示如何使用返回值获取输出范围的结束位置:

#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::rotate_copy

 C++20 引入的算法,用于将输入范围中的元素旋转后复制到输出范围。旋转操作会将某个中间位置的元素变为新范围的起点,原范围中该位置之后的元素移到前面,之前的元素移到后面。例如,将 [A, B, C, D, E] 以 C 为中间点旋转后得到 [C, D, E, A, B],然后将结果复制到目标范围。原始输入范围保持不变。

参数说明

  • firstlast:输入范围的迭代器对。
  • middle:旋转的中间点迭代器(新范围的起始位置)。
  • r:输入范围(可直接传递容器或视图)。
  • result:输出迭代器,指向目标范围的起始位置。

返回值

返回 rotate_copy_result<I, O>,包含两个成员:

  • in:输入范围的结束迭代器(即 last)。
  • out:输出范围的结束迭代器(指向最后一个复制的元素的下一个位置)。

示例 1:基本用法

将 [1, 2, 3, 4, 5] 以 3 为中间点旋转,得到 [3, 4, 5, 1, 2]

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst;

    // 找到中间点(值为 3 的元素)
    auto middle = std::ranges::find(src, 3);

    // 旋转并复制到 dst
    std::ranges::rotate_copy(src, middle, std::back_inserter(dst));

    // 输出结果
    for (int x : dst) {
        std::cout << x << " ";
    }
    // 输出:3 4 5 1 2
}

示例 2:自定义类型和投影

旋转自定义类型的范围,使用投影函数提取成员:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector<Person> src = {
        {"Alice", 30}, 
        {"Bob", 25}, 
        {"Charlie", 20}, 
        {"David", 35}
    };
    std::vector<Person> dst;

    // 找到年龄为 20 的人作为中间点
    auto middle = std::ranges::find(src, 20, &Person::age);

    // 旋转并复制到 dst
    std::ranges::rotate_copy(src, middle, std::back_inserter(dst));

    // 输出结果
    for (const auto& p : dst) {
        std::cout << p.name << " ";
    }
    // 输出:Charlie David Alice Bob
}

关键点

  1. 不修改原范围:输入范围保持不变,结果写入输出范围。
  2. 中间点迭代器middle 必须是输入范围内的有效迭代器。
  3. 复杂度:线性复杂度 O(N),N 为输入范围大小。
  4. 迭代器要求:输入范围需要是前向迭代器(支持多次遍历)。

对比 rotate 和 rotate_copy

| 特性 |                  std::ranges::rotate | std::ranges::rotate_copy |

是否修改原范围       是(原地旋转)              否

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

 典型用例               需要原地旋转的场景      需要保留原始数据的旋转副本的场景 |

std::ranges::unique_copy

 C++20 引入的算法,用于将输入范围内的元素复制到输出范围,同时跳过相邻连续的重复元素。它是 std::unique_copy 的范围化版本,支持更灵活的迭代器和范围操作。

功能

将输入范围 [first, last) 的元素复制到输出范围,跳过连续的重复元素。仅当相邻元素满足 pred 条件时,后者会被跳过。原始输入范围保持不变。

Defined in header <algorithm>

Call signature

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

          class Proj = std::identity,
          std::indirect_equivalence_relation<std::projected<I, Proj>>
              C = ranges::equal_to >
requires std::indirectly_copyable<I, O> && (std::forward_iterator<I> ||
             (std::input_iterator<O> && std::same_as<std::iter_value_t<I>,
                 std::iter_value_t<O>>) || std::indirectly_copyable_storable<I, O>)
constexpr unique_copy_result<I, O>

    unique_copy( I first, S last, O result, C comp = {}, Proj proj = {} );
(1)(since C++20)
template< ranges::input_range R, std::weakly_incrementable O,

          class Proj = std::identity,
          std::indirect_equivalence_relation<std::projected<ranges::iterator_t<R>,
              Proj>> C = ranges::equal_to >
requires std::indirectly_copyable<ranges::iterator_t<R>, O> &&
             (std::forward_iterator<ranges::iterator_t<R>> ||
             (std::input_iterator<O> && std::same_as<ranges::range_value_t<R>,
                 std::iter_value_t<O>>) ||
             std::indirectly_copyable_storable<ranges::iterator_t<R>, O>)
constexpr unique_copy_result<ranges::borrowed_iterator_t<R>, O>

    unique_copy( R&& r, O result, C comp = {}, Proj proj = {} );

参数说明

  • firstlast:输入范围的迭代器对。
  • r:输入范围(可直接传递容器或视图)。
  • result:输出迭代器,指向目标范围的起始位置。
  • pred:二元谓词(默认使用 std::ranges::equal_to),用于比较相邻元素是否相等。
  • proj:投影函数(可选),用于对元素进行转换后再比较。

返回值

返回 unique_copy_result<I, O>,包含两个成员:

  • in:输入范围的结束迭代器。
  • out:输出范围的结束迭代器(指向最后一个复制的元素的下一个位置)。

示例 1:基本用法(去除连续重复)

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> src = {1, 2, 2, 3, 3, 3, 4};
    std::vector<int> dst;

    // 去除连续重复元素,结果存入 dst
    std::ranges::unique_copy(src, std::back_inserter(dst));

    for (int x : dst) {
        std::cout << x << " ";
    }
    // 输出:1 2 3 4
}

示例 2:自定义谓词(不区分大小写去重)

将连续字母(不区分大小写)视为重复:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>
#include <cctype>

int main() {
    std::vector<char> src = {'A', 'a', 'B', 'b', 'C', 'c'};
    std::vector<char> dst;

    // 自定义谓词:不区分大小写比较
    auto case_insensitive_eq = [](char a, char b) {
        return std::tolower(a) == std::tolower(b);
    };

    std::ranges::unique_copy(
        src, 
        std::back_inserter(dst), 
        case_insensitive_eq
    );

    for (char c : dst) {
        std::cout << c << " ";
    }
    // 输出:A B C
}

示例 3:使用投影函数(自定义类型去重)

根据结构体的 id 字段去重:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iterator>

struct User {
    int id;
    std::string name;
};

int main() {
    std::vector<User> src = {
        {1, "Alice"}, 
        {1, "Alice"}, 
        {2, "Bob"}, 
        {3, "Charlie"}, 
        {3, "Charlie"}
    };
    std::vector<User> dst;

    // 投影函数:提取 User 的 id 字段进行比较
    std::ranges::unique_copy(
        src, 
        std::back_inserter(dst), 
        {}, // 默认谓词(相等比较)
        &User::id // 投影函数
    );

    for (const auto& user : dst) {
        std::cout << user.id << ":" << user.name << " ";
    }
    // 输出:1:Alice 2:Bob 3:Charlie
}

关键点

  1. 仅跳过连续重复:非连续的重复元素不会被去除。
  2. 不修改原范围:输入范围保持不变,结果写入输出范围。
  3. 复杂度:线性复杂度 O(N),N 为输入范围大小。
  4. 迭代器要求
    • 输入范围需要是前向迭代器(若需要多次遍历)。
    • 输出迭代器需支持写入操作(如 std::back_inserter 或预分配空间)。

对比 unique 和 unique_copy

| 特性 |              std::ranges::unique |     std::ranges::unique_copy |

是否修改原范围           是(原地去重)         否

输出目标                     无(原地操作)           指定输出范围

典型用例            需要原地去重的场景         需要保留原始数据的去重副本的场景 |


返回值使用示例

通过返回值获取输出范围的结束位置:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> src = {1, 2, 2, 3, 3, 4};
    std::vector<int> dst;
    dst.reserve(4);

    auto result = std::ranges::unique_copy(src, dst.begin());

    // 输出范围的有效元素为 [1, 2, 3, 4]
    
    // result.out 指向 dst.end()
    std::cout << (result.out == std::end(dst)) << std::endl; //输出 0
    std::cout << *(std::prev(result.out)) << std::endl;  //输出 4
    // result.in 指向 src.end()
    std::cout << (result.in == src.end()) << std::endl; 
    
}

;