C++—range

C++20 引入了 range 来简化对元素序列的处理(可以省略掉许多的循环遍历)。

1. range 和 view

range

range concept 通过提供一个迭代器以及一个哨兵来表示一个元素范围,以允许对某个类型进行遍历。

template<class T>
concept range = requires(T& t) {
  ranges::begin(t);
  ranges::end  (t);
};

如,vector 就是一个 range:

std::vector<int> vec{ 1, 2, 3 };

auto it1 = std::ranges::begin(vec);
auto it2 = std::ranges::end(vec);

view

view 是一个 range,且具有常数时间复杂度的的拷贝、移动和赋值操作(如,直接操作一对迭代器、或使用生成器来按需生成元素)。

template<class T>
concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;

template<class T>
inline constexpr bool enable_view =
    std::derived_from<T, view_base> || /*is-derived-from-view-interface*/<T>;

如,可通过 iota 来创建一个 view,类似于 Python 中的 range:

auto v1 = std::views::iota(1);        // [1, +inf)
auto v2 = std::views::iota(1, 10);    // [1, 10)

for (int i : v2)
{
    std::cout << i << ' ';
}

2. 范围工厂

会创建一个 view。

iota:创建一个不断递增的元素序列,可以是有界的,也可以是无界的。

#include <iostream>
#include <ranges>

int main()
{
    auto v1 = std::views::iota(10);        // [10, +inf)
    auto v2 = std::views::iota(1, 10);     // [1, 10)

    for (int i = 0; i < 10; i++)
    {
        std::cout << v1[i] << ' ';
    }
    std::cout << '\n';

    for (int i : v2)
    {
        std::cout << i << ' ';
    }
}
10 11 12 13 14 15 16 17 18 19
1 2 3 4 5 6 7 8 9

istream_view:对某个输入流不断地应用 operator >>,从而获得一系列的元素。

#include <iostream>
#include <sstream>
#include <ranges>

int main()
{
    std::istringstream nums("1.1 2.2 3.3\t4.4\n5.5");
    auto v = std::ranges::istream_view<float>(nums);

    for (float f : v)
    {
        std::cout << f << ", ";
    }
}
1.1, 2.2, 3.3, 4.4, 5.5,

3. 范围适配器

接受一个 range,对其执行某些操作后,返回结果 view。

counted:从指定位置开始获取 n 个元素。

std::vector<int> vec{ 1, 2, 3, 4, 5, 6, 7 };

for (int i : std::views::counted(vec.begin() + 1, 3))
{
    std::cout << i << ' ';
}
2 3 4

drop:丢弃 range 中的前 n 个元素。

int nums[] = { 0, 1, 2, 3, 4, 5, 6 };

for (int i : std::views::drop(nums, 3))
{
    std::cout << i << ' ';
}
3 4 5 6

elements:获取第 n 列元素。

注:view 中的元素需要是 tuple-like 的。

std::vector<std::tuple<int, int, int>> vecs
{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
};

for (int i : std::views::elements<1>(vecs))
{
    std::cout << i << ' ';
}
2 5 8

filter:对 range 进行过滤,只保留符合条件的元素。

int nums[] = { 0, 1, 2, 3, 4, 5 };
auto even = [](int i)
{
    return i % 2 == 0;
};

for (int i : std::views::filter(nums, even))
{
    std::cout << i << ' ';
}
0 2 4

join:合并多个 range。

std::vector<int> vec1{ 1, 2, 3 };
std::vector<int> vec2{ 4, 5, 6 };
std::vector<std::vector<int>> vecs{vec1, vec2};

for (int i : std::views::join(vecs))
{
    std::cout << i << ' ';
}
1 2 3 4 5 6

reverse:反转 range 中的元素。

const int a[] = { 1, 2, 3, 4, 5, 6, 7 };

for (int i : std::views::reverse(a))
{
    std::cout << i << ' ';
}
7 6 5 4 3 2 1

split:按指定的元素分割 range。

std::vector<int> vec{ 1, 2, 3, 4, 5, 6, 7 };

for (const auto& v : std::views::split(vec, 4))
{
    for (int i : v)
    {
        std::cout << i << ' ';
    }
    std::cout << '\n';
}
1 2 3
5 6 7

take:获取 range 中的前 n 个元素。

int nums[] = { 0, 1, 2, 3, 4, 5 };

for (int i : std::views::take(nums, 3))
{
    std::cout << i << ' ';
}
0 1 2

transform:对 range 中的每个元素执行指定的转换操作,转换操作的返回值就是结果 view 中的元素。

int nums[] = { 0, 1, 2, 3, 4, 5 };
auto square = [](int i)
{
    return i * i;
};

for (int i : std::views::transform(nums, square))
{
    std::cout << i << ' ';
}
0 1 4 9 16 25

4. 管道运算符

如果 C 是一个范围适配器闭包对象R 是一个可以转换为 view 的 range,则 C(R) 等价于 R | C

范围适配器闭包对象包括:一元范围适配器(只接受一个参数)、绑定了余下参数的多元范围适配器(只留下第一个参数未指定)和 R | C 的返回值。

int nums[] = { 0, 1, 2, 3, 4, 5 };

auto even = [](int i)
{
    return i % 2 == 0;
};

// 先过滤,然后反转过滤得到的元素
for (int i : nums | std::views::filter(even) | std::views::reverse)
{
    std::cout << i << ' ';
}
4 2 0

以下等价:

R | C | D     // (R | C) | D
R | (C | D)
7 声望
0 粉丝
0 条评论
推荐阅读
Python—文件系统操作
1. 路径操作查看路径是否存在: {代码...} 拼接路径: {代码...} 查看文件类型: {代码...} 2. 目录操作创建目录: {代码...} 删除目录: {代码...} 读取目录: {代码...} {代码...} 当前工作目录: {代码...} 拷...

gzming阅读 1.2k

程序员适合创业吗?
大家好,我是良许。从去年 12 月开始,我已经在视频号、抖音等主流视频平台上连续更新视频到现在,并得到了不错的评价。每个视频都花了很多时间精力用心制作,欢迎大家关注哦~考虑到有些小伙伴没有看过我的视频,...

良许3阅读 1.3k

比cat更好用的命令!
但 cat 命令两个很重大的缺陷:1. 不能语法高亮输出;2. 文本太长的话无法翻页输出。正是这两个不足,使得 cat 只能用来查看行数不多的小文件。

良许2阅读 728

DBoS 系统说明
程序员TianSong以单片机开发入门,后续又做了 Qt 相关工作,有时间后开始进行 linux 相关的学习,恰巧在二一年十一月份,百问网的韦东山老师进行了三个月的 linux 驱动直播,于是有了开发 DBoS 的念头。

TianSong1阅读 1.2k

【Qt】简单桌面
[链接]简介简单桌面是一款小巧便捷的桌面背景管理软件。由编程爱好者个人开发,不收集使用者个人信息、不连接网络、不弹窗。下载功能支持单静态图片及多静态图片轮播(轮播时间可设置)支持GIF动画背景支持视频背...

TianSong3阅读 2.2k

良许翻天覆地的2022年
大家好,我是良许,新年快乐呀~在我女室友坚持不懈的努力之下,2022年的最后一天我终于被她传染了,阳了~此时的我,正顶着37多度的低烧写下这篇年终总结。2022年,对于大多数人而言,封控是主旋律——不停地核酸,...

良许2阅读 793评论 1

一个Bug让人类科技倒退几十年?
大家好,我是良许。前几天在直播的时候,问了直播间的小伙伴有没人知道「千年虫」这种神奇的「生物」的,居然没有一人能够答得上来的。所以,今天就跟大家科普一下这个人类历史上最大的 Bug 。1. 全世界的恐慌一...

良许阅读 1.4k

7 声望
0 粉丝
宣传栏