前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++20四大特性之Ranges

C++20四大特性之Ranges

作者头像
公众号guangcity
发布2024-03-12 18:20:49
1410
发布2024-03-12 18:20:49
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

C++20 Ranges

  • 1.基础概念
  • 2.使用

那么,本篇将开始学习另外一个特性ranges。

ranges是C++20的主要特性之一,其中"view"是比较重要的一部分。C++20之前,标准库的算法实现是基于迭代器来实现的,例如:std::sort。

代码语言:javascript
复制
std::sort(v.begin() + 2, v.end())

迭代器 + 算法能够完成一些复杂的操作,例如:我想要倒这排序:

代码语言:javascript
复制
std::sort(v.rbegin(), v.rend())

但是它也伴随着一些问题:

  • 容易混用两个不兼容的迭代器。
  • 算法的组合能力太弱,需要存储一些中间变量

例如:现在有一个学生信息系统,我们想要计算年龄在21-25区间且GPA >= 3.5,求取满足前面条件的学生总GPA。C++17之前我们可以写出下面这样的伪代码:

代码语言:javascript
复制
std::vector<Student> students
std::vector<Student> selected;
std::copy_if(students.begin(), students.end(), std::back_inserter(selected),
             [](const Student& student) {
               return student.age >= 21 && student.age <= 25 && student.gpa >= 3.5;
             });

double s = std::accumulate(
    selected.begin(), selected.end(), 0.0,
    [](double sum, const Student& student) { return sum + student.gpa; });

可以看到我们在过滤前面满足条件的学生信息时需要将其拷贝到selected,然后再对其求和。

C++20 引入了一种更为简洁、高效的写法,通过使用范围和管道操作符 | 连接多个操作,可以在不需要中间变量的情况下直接求和,例如:

代码语言:javascript
复制
double s = 0.0;
for (const auto& student :
     students | std::views::filter([](const auto& s) {
       return s.age >= 21 && s.age <= 25 && s.gpa >= 3.5;
     }) | std::views::transform([](const auto& s) { return s.gpa; })) {
  s += student;
}

可以看到多个算法之间无缝衔接,减少了中间临时变量的内存分配,其可读性也大大增加,通过管道|将不同的操纵连接在一块。

接下来,让我们一起探讨C++20 ranges相关的内容。

1.基础概念

1.range

range 是一种表示一个序列的抽象概念。它可以是任何具有迭代器的容器或者是一个定义了 begin()end() 函数的对象。如 std::vectorstd::list 等都是范围的例子。对于数组,也可以视为范围。

2.view

view 是对 Range 的一种只读访问。它是一种惰性计算的方式,只有在需要的时候才会进行计算,这意味着它并不实际存储数据。例如:std::views::filterstd::views::transform 就是view的典型例子。它们允许我们对 range 进行筛选和转换,而不必实际创建新的容器。

3.algorithm

算法是对range或view进行操作的函数,例如:std::sortstd::find 等都是算法的例子。

4.|

管道操作符|,可以将视图与算法链接起来,将左侧的结果作为右侧的输入。它使得代码更为清晰、简洁。例如:students | std::views::filter(...)students 范围传递给 std::views::filter 进行过滤操作,然后再将结果传递给后续的操作。

以上面的student计算为示例,在这个例子中我们使用了范围students通过|作为视图filter的输入,然后将结果作为视图transform的输入,最后返回一个范围,基于这个范围进行循环,通过累加算法求和得到结果。

代码语言:javascript
复制
double s = 0.0;
for (const auto& student :
     students | std::views::filter([](const auto& s) {
       return s.age >= 21 && s.age <= 25 && s.gpa >= 3.5;
     }) | std::views::transform([](const auto& s) { return s.gpa; })) {
  s += student;
}

可以看到,使用range有如下好处:

  • 提高代码可读性
  • 懒惰计算:Ranges 引入了视图(view)的概念,允许懒惰计算,即在需要时才计算结果。

例如:只有在*v.begin()时才会去计算。

代码语言:javascript
复制
auto v = std::views::reverse(vec);
std::cout << *v.begin() << std::endl;
  • 减少错误:Ranges 的设计有助于减少一些传统迭代器操作中容易出现的错误,例如使用不兼容的迭代器。
  • 等等。

范围概念引入了不同的概念来描述不同类型的范围。这些概念有助于在泛型编程中更好地理解和限制范围的特性。以下是一些常用的范围概念:

https://en.cppreference.com/w/cpp/ranges

概念

描述

容器举例

std::ranges::input_range

可以从头到尾至少迭代一次

std::forward_list、std::list、std::duque、std::array、std::vector

std::ranges::forward_range

可以从头到尾迭代多次

std::forward_list、std::list、std::duque、std::array、std::vector

std::ranges::bidirectional_range

迭代器也可以向后移动--

std::list、std::duque、std::array、std::vector

std::ranges::random_access_range

你可以在常数时间内跳转到元素 []

std::duque、std::array、std::vector

std::ranges::contiguous_range

元素总是连续存储在内存中

std::array、std::vector

2.使用

使用这个特性比较简单,只需要引入头文件,使用接口即可。

例如:过滤一堆数字当中的偶数。

代码语言:javascript
复制
#include <ranges>
auto evenNumbers = numbers | std::views::filter([](int x) { return x % 2 == 0; });

编译:指定-std即可。

代码语言:javascript
复制
g++ -std=c++20 main.cc -o main

编译器支持可以阅读下面清单:

https://en.cppreference.com/w/cpp/compiler_support/20

gcc >=10,clang >=13(partial),>=15全面支持

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++20 Ranges
    • 1.基础概念
      • 2.使用
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档