C++ 20新特性之Ranges

C++ 20新特性之Ranges任何可以被迭代的对象都可以视为 Ranges 包括标准库容器 比如 vector list map 等 数组 甚至是由函数生成的连续或非连续元素序列

大家好,欢迎来到IT知识分享网。

💡 如果想阅读最新的文章,或者有技术问题需要交流和沟通,可搜索并关注微信公众号“希望睿智”。

为什么要引入Ranges

        在C++ 20之前,我们处理容器和迭代器时,通常需要使用一系列的算法和复杂的迭代器操作。这不仅繁琐,而且很容易出错,尤其是对于复杂的算法组合。C++ 20引入Ranges的原因主要是为了提高标准库的表达力、效率和代码的可读性,以及简化序列操作的复杂度。

        任何可以被迭代的对象都可以视为Ranges,包括标准库容器(比如:vector、list、map等)、数组,甚至是由函数生成的连续或非连续元素序列。Ranges提供了一组函数和操作符,让我们能够以更加函数式的方式处理容器中的数据。这些函数和操作符通常与现有的STL算法具有相同的功能,但它们更简洁,也更易于使用。

Ranges视图

        View是Ranges的核心,相当于一个透镜,通过它可以观察Ranges的不同视图。视图是一个对数据的非拥有、可能懒加载的引用,它不会复制或修改原始数据,而是提供了一种方式来查看或处理这些数据。这使得我们可以在不修改原始数据的情况下,对数据进行过滤、映射、切片等各种复杂的操作。

        在下面的示例代码中,我们使用views::filter来过滤出一个整型容器中的偶数,得到了视图viewEven。最后,我们使用ranges::for_each来遍历视图viewEven进行输出。

#include <iostream> #include <vector> #include <ranges> #include <algorithm> using namespace std; int main() { vector<int> vctNum = {1, 2, 3, 4, 5}; auto viewEven = vctNum | views::filter([](int n) { return n % 2 == 0; }); // 输出: 2 4 ranges::for_each(viewEven, [](int n) { cout << n << ' '; }); return 0; }

Ranges适配器

        Ranges适配器负责接收一个输入Range(或多个输入Ranges),并根据适配器定义的规则转换或处理这些输入,最终产生一个新的Range。这个过程通常是懒惰执行的,意味着数据仅在实际访问时才会被处理。常见的适配器有如下几种。

        转换适配器(Transform Adapter):比如ranges::view::transform,它接受一个函数或Lambda,对输入Range中的每个元素应用该函数,并生成一个新元素。

        过滤适配器(Filter Adapter):比如ranges::view::filter,它根据提供的谓词筛选出符合条件的元素,丢弃不符合条件的元素。

        取前N个元素适配器(Take Adapter):比如ranges::view::take,仅取Range的前N个元素,这对于处理有限制的数据流非常有用。

        丢弃前N个元素适配器(Drop Adapter):比如ranges::view::drop,丢弃Range的前N个元素,然后返回剩余的元素。

        合并适配器(Concatenate Adapter):比如ranges::view::join,用于将一系列Range(通常为Range的Range)连接成一个单一的Range。

        切片适配器(Slicing Adapter):比如ranges::view::slice,可以取出Range的一个子区间,类似于数组的切片操作。

        在下面的示例代码中,我们使用转换适配器将原始的vctNum转换成了一个视图,这个视图中每个元素的值为vctNum中每个元素的平方。最后,我们使用ranges::for_each来遍历视图viewSquared进行输出。注意:views::transform并不会修改原始的vctNum,而是产生了一个新的Range视图。

#include <iostream> #include <vector> #include <ranges> #include <algorithm> using namespace std; int main() { vector<int> vctNum = {1, 2, 3, 4, 5}; auto viewSquared = vctNum | views::transform([](int n) { return n * n; }); // 输出: 1 4 9 16 25 ranges::for_each(viewSquared, [](int n) { cout << n << ' '; }); return 0; }

Ranges算法

        Ranges还提供了一组与STL算法类似的算法,这些算法可以直接在Range上操作,而无需显式地使用繁琐的迭代器。在下面的示例代码中,我们使用std::ranges::find来在vctNum中查找一个元素。

#include <iostream> #include <vector> #include <ranges> #include <format> using namespace std; int main() { vector<int> vctNum = {1, 2, 3, 4, 5}; auto it = ranges::find(vctNum, 2); if (it != vctNum.end()) { cout << format("found {}", *it) << endl; } else { cout << "not found" << endl; } return 0; }

管道操作符

        管道操作符,也就是“|”符号,是C++ 20中一个重载过的操作符。在Ranges库中,它用于连接一系列的操作,形成一个数据处理的管道。这意味着,数据可以从一个操作流向下一个操作,每个环节只关注处理数据,而不需要关心数据的来源和去向。当我们将numbers | ranges::view::…这样的表达式放在一起时,意味着我们正在创建一个处理numbers集合的管道,其中…代表具体的View操作。

        在下面的示例代码中,我们首先过滤出偶数,然后计算它们的平方,这展示了ranges的简洁性和表达力。通过这样的管道表达式,C++程序员能够以一种声明性的方式写出高效且易于理解的代码,特别适合进行数据处理和分析任务。

#include <iostream> #include <vector> #include <ranges> #include <algorithm> using namespace std; int main() { vector<int> vctNum = {1, 2, 3, 4, 5, 6}; auto viewResult = vctNum | views::filter([](int x) { return x % 2 == 0; }) | views::transform([](int x) { return x * x; }); // 输出:4 16 36 ranges::for_each(viewResult, [](int x) { cout << x << " "; }); return 0; }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/147847.html

(0)
上一篇 2025-04-04 16:26
下一篇 2025-04-04 16:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信