跳转至

第7章 范围和视图的工具

7.1 范围作为视图的实用工具

7.1.1 std::views::all()

std::views::all(rg): + 如果rg已经是视图, 则为rg的副本 + 否则, rgstd::ranges::ref_view是一个左值 + 否则, 若rgstd::ranges::owned_view是右值(未命名的临时对象或用std::move标记的范围对象)

7.1.1.1 std::views::all_t<>类型

std::vector<int> v{0, 8, 15, 47, 11, -1, 13};
std::views::all_t<decltype(v)> a1{v}; // ERROR
std::views::all_t<decltype(v)&> a2{v}; // ref_view<vector<int>>
std::views::all_t<decltype(v)&&> a3{v}; // ERROR
std::views::all_t<decltype(v)> a4{std::move(v)}; // owning_view<vector<int>>
std::views::all_t<decltype(v)&> a5{std::move(v)}; // ERROR
std::views::all_t<decltype(v)&&> a6{std::move(v)}; // owning_view<vector<int>>

7.1.2 std::views::counted()

std::views::counted(beg, sz)创建一个以beg开头的范围的前sz个元素, 由开发者保证begsz是有效的.

注意, 为了性能, std::views::counted()的返回类型有所不一样: + contiguous_itertor 则返回 std::span, 其实就是内存连续的的容器,适用于std::vector,std::array, 原始指针, 原始数组, 迭代器 + std::random_access_iterator则返回std::views::subrange且迭代器是std::random_access_iterator,适用于std::deque + 否则返回std::views::subrange, 以std::default_sentinel_t类型的哨兵作为结束

7.1.3 std::views::common()

范围适配器std::views::common()为传递的范围生成一个**具有统一类型**的视图, 用于开始迭代器和哨兵(结束迭代器). 其行为类似于范围适配器 std::views::all(),若其迭代器具有不同的类型,则会从传递的参数创建std::ranges::common_view

template<typename BegT, typename EndT>
void callAlgo(BegT beg, EndT end) {
    auto v = std::views::common(std::ranges::subrange(beg, end));
    algo(v.begin(), v.end());
}

7.2 新迭代器类别

概念 约束 供应者
std::input_iterator 可读取,可递增,支持解引用 istream 迭代器,单向输入流
std::output_iterator 可写入,可递增,支持赋值 ostream 迭代器,插入器
std::forward_iterator 继承 input_iterator,支持多次遍历 std::forward_list<>,无序容器迭代器
std::bidirectional_iterator 继承 forward_iterator,支持双向移动 std::list<>,std::set<>,std::map<>
std::random_access_iterator 继承 bidirectional_iterator,支持随机访问 std::deque<>,支持 +,-,<,> 运算
std::contiguous_iterator 继承 random_access_iterator,元素在连续内存中 std::vector<>,std::array<>,原始指针

新增一个iterator_concept, 并定义可以设置该属性来表示一个不同于传统类别的C++20类别.

std::vector vec{1, 2, 3, 4};
auto pos1 = vec.begin();
decltype(pos1)::iterator_category // type std::random_access_iterator_tag
decltype(pos1)::iterator_concept // type std::contiguous_iterator_tag
auto v = std::views::iota(1);
auto pos2 = v.begin();
decltype(pos2)::iterator_category // type std::input_iterator_tag
decltype(pos2)::iterator_concept // type std::random_access_iterator_tag

有效的 C++20 输入迭代器可能根本不是 C++17 迭代器 (例如,不提供复制)。对于这些迭代器, 传统的迭代器特性不起作用。由于这个原因,从 C++20 开始: • 使用 std::iter_value_t 代替 iterator_traits<>::value_type. • 使用 std::iter_reference_t 代替 iterator_traits<>::reference. • 使用 std::iter_difference_t 代替 iterator_traits<>::difference_type.

7.3 新增迭代器和哨兵类型

• std::counted_iterator 用于迭代器,该迭代器本身有一个计数来指定范围的结束 • std::common_iterator 用于公共迭代器类型,可用于不同类型的两个迭代器 • std::default_sentinel_t 用于结束迭代器,强制迭代器检查其结束 • std::unreachable_sentinel_t 表示永远无法到达的 end 迭代器,表示无限范围 • std::move_sentinel 用于将副本映射到 move 的 end 迭代器

7.3.1 std::counted_iterator

7.3.1 std::common_iterator

7.3.1 std::default_sentinel_t

7.3.1 std::unreachable_sentinel_t

7.3.1 std::move_sentinel

7.4 处理范围的新函数

函数 意义
std::ranges::empty(rg) 生成的范围是否为空
std::ranges::size(rg) 生成范围的大小
std::ranges::ssize(rg) 将范围的大小作为 signed 类型的值
std::ranges::begin(rg) 生成指向该范围的第一个元素的迭代器
std::ranges::end(rg) 生成范围哨兵(一个迭代器)
std::ranges::cbegin(rg) 生成指向范围第一个元素的常量迭代器
std::ranges::cend(rg) 生成范围的一个常量哨兵(一个到末尾的常量迭代器)
std::ranges::rbegin(rg) 生成范围内第一个元素的反向迭代器
std::ranges::rend(rg) 生成范围的反向前哨(一个迭代器到末尾)
std::ranges::crbegin(rg) 生成指向范围第一个元素的反向常量迭代器
std::ranges::crend(rg) 生成范围的反向常量哨兵(一个到末尾的常量迭代器)
std::ranges::data(rg) 生成该范围的原始数据
std::ranges::cdata(rg) 生成具有 const 元素的范围的原始数据

表 7.3 用于处理范围元素的泛型函数

7.5 用于处理范围的新类型函数/工具

7.5.1 范围的泛型类型

7.6 范围算法