std::ranges::views::common, std::ranges::common_view
std::ranges::views::common, std::ranges::common_view
C++20 引入的用于将范围适配为“通用范围”的工具,主要解决某些算法需要传统迭代器对(如 begin
和 end
类型相同)的问题。
基本概念
1. 功能
- 适配传统算法:将范围(range)的迭代器和哨兵(sentinel)类型统一,使其适配需要“迭代器对”的旧式算法(如 C++17 之前的算法)。
- 类型一致性:确保
begin()
和end()
返回的迭代器类型相同。 - 零开销原则:仅在必要时添加类型转换,无额外内存分配。
以下是详细说明和示例:
核心概念
- 问题背景:C++20 的某些范围(如
split_view
)的begin
和end
返回的迭代器类型可能不同,但传统算法(如 C++17 前的算法)需要它们类型相同。 - 解决方案:
common_view
将范围适配为begin
和end
迭代器类型相同的“通用范围”。
std::ranges::common_view
- 定义:
template<std::ranges::view V> class common_view : public std::ranges::view_interface<common_view<V>>
- 适用场景:
- 当原始范围的迭代器和哨兵类型不同,但需要转换为相同类型时。
- 需要将范围传递给传统算法(如接受
int*
和int*
的算法)。
语法与参数
1. 语法
auto common_view = range | std::ranges::views::common;
range
:输入范围(如容器、视图)。
示例 1:适配 split_view
#include <ranges>
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::string str = "hello,world,cpp20";
auto split = str | std::views::split(',');
// split 的迭代器和哨兵类型不同,无法直接传递给传统算法
// 使用 common_view 适配
auto common_range = split | std::views::common;
// 现在可以像传统范围一样使用
for (const auto& part : common_range) {
for (char c : part) std::cout << c;
std::cout << '\n';
}
}
示例 2:传递给传统算法
某些传统算法(如 std::sort
)要求 begin()
和 end()
的迭代器类型严格一致,但 C++20 的某些范围(如 std::ranges::iota_view
)可能返回不同类型的哨兵:
示例 2:处理子范围
#include <ranges>
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto reversed = vec | std::views::reverse;
// 传统算法需要 begin 和 end 类型相同
// 使用 common_view 适配
auto common_reversed = reversed | std::views::common;
// 使用 std::sort(仅示例,反转后排序可能无意义)
std::ranges::sort(common_reversed); // 需要迭代器类型相同
for (int x : common_reversed) {
std::cout << x << ' '; // 输出:1 2 3 4 5(排序后的反转)
}
}
std::ranges::views::common
- 定义:范围适配器对象,可通过管道操作符
|
简化common_view
的创建。 - 等效操作:
views::common(r)
等价于common_view<views::all_t<decltype(r)>>{r}
。
示例 3:结合管道操作符
#include <ranges>
#include <iostream>
#include <list>
#include <algorithm> // 必须包含此头文件以使用 std::find
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
auto even = lst | std::views::filter([](int x) { return x % 2 == 0; });
// 使用 views::common 适配
auto common_even = even | std::views::common;
// 传递给传统算法 std::find
if (std::find(common_even.begin(), common_even.end(), 4) != common_even.end()) {
std::cout << "Found 4\n";
}
}
注意事项
- 性能:
common_view
可能引入额外开销(如缓存迭代器),需谨慎使用。 - 适用性:仅在需要传统迭代器对时使用,现代算法(如
std::ranges::sort
)通常不需要。 - 生命周期:确保底层范围的生命周期长于
common_view
。
总结
common_view
:将begin
和end
迭代器类型不同的范围适配为类型相同的通用范围。views::common
:通过管道操作符简化common_view
的创建。- 典型场景:与传统算法交互,或需要迭代器类型一致的操作。