std::ranges::copy
C++20 引入的范围库(Ranges Library)中的算法,用于将输入范围(如容器、数组或视图)的元素复制到指定的输出位置。它是对传统 std::copy
的增强,支持更简洁的语法和范围操作。
基本功能
- 作用:将源范围的元素复制到目标范围。
- 返回值:包含复制结束位置的迭代器(即源范围的结束迭代器和目标范围的结束迭代器)。
- 要求:
- 源范围和目标范围必须有效。
- 目标范围必须有足够的空间容纳源范围的元素。
Defined in header | ||
Call signature | ||
template< std::input_iterator I, std::sentinel_for<I> S, std::weakly_incrementable O > requires std::indirectly_copyable<I, O> | (1) | (since C++20) |
template< ranges::input_range R, std::weakly_incrementable O > requires std::indirectly_copyable<ranges::iterator_t<R>, O> |
- 参数:
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:返回值详解
-
.in
表示源范围的结束迭代器(即复制结束后的位置)。
对于完整范围复制,.in
等于源范围的end()
迭代器。 -
.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
返回值用途
-
检查复制完整性
通过比较.in
和源范围的end()
,可以判断是否全部复制成功。if (result.in == src.end()) { std::cout << "全部元素已复制" << std::endl; }
-
继续写入目标范围
使用.out
可以继续向目标范围追加数据:std::vector<int> additional = {6, 7, 8}; std::ranges::copy(additional, result.out);
总结
.in
和.out
分别表示源范围和目标范围的结束位置。- 返回值可用于验证复制完整性、处理部分复制或继续操作目标范围。
与传统 std::copy
的区别
特性 | std::copy | std::ranges::copy |
---|---|---|
参数 | 迭代器对 (first, last) | 直接接受范围 r |
返回值 | 输出结束迭代器 | copy_result 结构体 |
范围适配器支持 | 需手动组合迭代器 | 支持管道操作符 ` |
与 std::ranges::
copy_backward
的区别
std::ranges::copy
std::ranges::copy_backward
复制方向 从前向后 从后向前
迭代器类型 输入迭代器 双向迭代器
重叠处理 目标起始位置在源前面时安全 目标结束(dst.end())位置在源后面时安全
或者说目前范围最后一个复制元素的位置
在原范围之内的话是安全的。
注意事项
- 目标空间:确保输出迭代器指向的位置有足够的空间,或使用
std::back_inserter
动态扩展容器。 - 性能:范围适配器(如
filter
、transform
)是惰性求值的,不会产生额外开销。 - 兼容性:需要编译器支持 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, | (3) | (since C++20) |
template< ranges::input_range R, std::weakly_incrementable O, class Proj = std::identity, |
- 参数:
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_if | std::ranges::copy_if |
---|---|---|
参数 | 迭代器对 (first, last) | 直接接受范围 r |
投影支持 | 无 | 支持 proj 参数 |
返回值 | 输出结束迭代器 | copy_if_result 结构体 |
范围适配器集成 | 需手动组合迭代器 | 支持管道操作符 ` |
注意事项
- 输出空间:确保输出迭代器指向的位置有足够空间,或使用
std::back_inserter
动态扩展容器。 - 谓词与投影:
- 谓词应接受投影后的值类型(若使用投影)。
- 避免在谓词或投影中修改元素(除非明确需要副作用)。
- 兼容性:需要编译器支持 C++20(如 GCC 10+、Clang 13+)。
总结
std::ranges::copy_if
通过以下特性提升代码质量:
- 简洁性:直接操作范围,无需手动处理迭代器。
- 灵活性:支持投影和范围适配器,便于组合复杂操作。
- 安全性:返回值明确标识输入和输出的结束位置。
std::ranges::copy_n
C++20 引入的范围库算法,用于从输入范围复制指定数量的元素到目标位置。它是传统 std::copy_n
的范围化版本,提供更简洁的语法和更强的类型安全性。
Defined in header | ||
Call signature | ||
template< std::input_iterator I, std::weakly_incrementable O > requires std::indirectly_copyable<I, O> | (1) | (since C++20) |
Helper type | ||
template< class I, class 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
注意事项
- 输入范围有效性:必须确保输入范围至少有
n
个元素,否则行为未定义。 - 目标空间:目标位置必须有足够空间容纳
n
个元素,或使用std::back_inserter
动态扩展容器。 - 迭代器类型:
- 输入迭代器
I
需满足std::input_iterator
。 - 输出迭代器
O
需满足std::weakly_incrementable
(允许写入操作)。
- 输入迭代器
应用场景
- 提取固定长度的数据片段(如读取缓冲区的前 N 字节)。
- 复制容器的部分内容到另一个容器。
- 初始化目标范围的前 N 个元素。
通过 std::ranges::copy_n
,可以高效且安全地实现元素的批量复制操作。
std::ranges::copy_backward
C++20 引入的算法,用于将范围中的元素从后向前复制到目标位置(目标范围也是从后向前赋值)。它适用于需要避免覆盖源范围中未复制元素的情况(例如目标范围与源范围有重叠且目标起始位置在源范围中间。
Defined in header | ||
Call signature | ||
template< std::bidirectional_iterator I1, std::sentinel_for<I1> S1, std::bidirectional_iterator I2 > | (1) | (since C++20) |
template< ranges::bidirectional_range R, std::bidirectional_iterator I > requires std::indirectly_copyable<ranges::iterator_t<R>, I> |
-
参数:
first
,last
:输入范围的起始和结束迭代器(左闭右开区间[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 << " ";
}
}
注意事项
- 迭代器方向:输入和目标的迭代器必须是双向迭代器(支持
--
操作)。 - 目标空间:目标范围必须有足够的空间容纳
last - first
个元素。 - 重叠处理:当源范围和目标范围重叠时:
- 如果目标起始位置在源范围的前面,使用
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
结构体,包含两个迭代器:
in
: 指向源范围中最后一个被复制的元素的下一个位置(即源范围的结束迭代器)。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 | ||
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 > | (since C++20) (until C++26) | |
template< std::input_iterator I, std::sentinel_for<I> S, class O, class Proj = std::identity, | (since C++26) | |
(2) | ||
template< ranges::input_range R, class T1, class T2, std::output_iterator<const T2&> O, class Proj = std::identity > | (since C++20) (until C++26) | |
template< ranges::input_range R, class O, class Proj = std::identity, |
参数说明
first
,last
:输入范围的迭代器对。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);
参数说明
first
,last
:输入范围的迭代器对。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 }
解释
- 输入范围:
src
是{1, 2, 3, 2, 5}
。 - 替换规则:将所有值为
2
的元素替换为99
。 - 输出范围:通过
std::back_inserter(dst)
将结果插入到dst
容器。 - 结果:
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, | (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> | (since C++26) | |
(4) | ||
template< ranges::input_range R, class T, std::output_iterator<const T&> O, | (since C++20) (until C++26) | |
template< ranges::input_range R, class O, class T = std::iter_value_t<O> |
参数说明
first
,last
:输入范围的迭代器对。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 !
}
关键点
- 灵活的条件判断:通过谓词
pred
可以定义任意复杂的替换条件。 - 投影函数:
proj
允许先对元素进行转换(如提取成员变量),再应用谓词。 - 不修改原范围:输入范围保持不变,结果写入输出范围。
- 复杂度:线性复杂度 O(N),N 为输入范围大小。
std::ranges::reverse_copy
C++20 引入的算法,用于将输入范围内的元素逆序复制到输出范围。它是 std::reverse_copy
的范围化版本,支持更灵活的迭代器和范围操作。不修改原范围。
功能
将输入范围 [first, last)
的元素逆序复制到输出范围,原始输入范围保持不变。
Defined in header | ||
Call signature | ||
template< std::bidirectional_iterator I, std::sentinel_for<I> S, std::weakly_incrementable O > | (1) | (since C++20) |
template< ranges::bidirectional_range R, std::weakly_incrementable O > requires std::indirectly_copyable<ranges::iterator_t<R>, O> | (2) | (since C++20) |
Helper types | ||
template< class I, class O > |
参数说明
first
,last
:输入范围的迭代器对(需满足双向迭代器)。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)
}
关键点
- 不修改原范围:输入范围保持不变,结果写入输出范围。
- 输出迭代器:需确保输出范围有足够空间(或使用
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
迭代器继续向输出范围写入其他数据。
示例 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]
,然后将结果复制到目标范围。原始输入范围保持不变。
参数说明
first
,last
:输入范围的迭代器对。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
}
关键点
- 不修改原范围:输入范围保持不变,结果写入输出范围。
- 中间点迭代器:
middle
必须是输入范围内的有效迭代器。 - 复杂度:线性复杂度 O(N),N 为输入范围大小。
- 迭代器要求:输入范围需要是前向迭代器(支持多次遍历)。
对比 rotate
和 rotate_copy
| 特性 | std::ranges::rotate
| std::ranges::rotate_copy
|
是否修改原范围 是(原地旋转) 否
输出目标 无(原地修改) 指定输出范围
典型用例 需要原地旋转的场景 需要保留原始数据的旋转副本的场景 |
std::ranges::unique_copy
C++20 引入的算法,用于将输入范围内的元素复制到输出范围,同时跳过相邻连续的重复元素。它是 std::unique_copy
的范围化版本,支持更灵活的迭代器和范围操作。
功能
将输入范围 [first, last)
的元素复制到输出范围,跳过连续的重复元素。仅当相邻元素满足 pred
条件时,后者会被跳过。原始输入范围保持不变。
参数说明
first
,last
:输入范围的迭代器对。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
}
关键点
- 仅跳过连续重复:非连续的重复元素不会被去除。
- 不修改原范围:输入范围保持不变,结果写入输出范围。
- 复杂度:线性复杂度 O(N),N 为输入范围大小。
- 迭代器要求:
- 输入范围需要是前向迭代器(若需要多次遍历)。
- 输出迭代器需支持写入操作(如
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;
}