C++20 Ranges链式流处理编译报错,如何实现类Stream的过滤、遍历、计数
C++20 Ranges 实现等价流处理的正确方式
编译错误的核心根源
C20 Ranges的组件设计和Java Stream、第三方C Stream库的统一链式API思路不一样,它把流处理的能力拆成了两类边界清晰的组件,用法不能混:
- 视图适配器(统一带
views::前缀,比如views::filter、views::transform):属于惰性计算组件,只定义数据处理规则,不会真正触发元素遍历,天生支持通过|管道符链式拼接 - 终端算法(比如
ranges::for_each、ranges::count、ranges::distance):属于实际启动遍历的即时计算组件,在C++20标准中这些函数是不支持直接通过管道拼接在视图后面的,必须把要处理的容器/视图作为第一个参数传给函数
你写的代码正是把这两类组件的用法搞混了,才会报匹配不到函数的错。
两个报错的具体原因
ranges::for_each报错:这个函数的标准要求是至少传两个参数:要遍历的range、对每个元素执行的回调。你只把回调通过管道接在后面,既没给函数传待处理的range,函数本身也不是可管道拼接的视图组件,编译器自然找不到匹配的重载,提示“候选需要3个参数,只给了1个”就是这个原因。ranges::count()报错:这个函数的功能是“数一下range里有多少个元素等于指定的目标值”,必须传两个参数:待统计的range、要匹配的目标值。你写的空括号调用既没传range,也没说要统计啥,根本找不到对应的函数。而且本身这个函数就不适合用来数过滤后视图的总元素数——对于没法提前算出大小的过滤视图,要数元素总数应该用ranges::distance算迭代器距离,或者在单次遍历的时候累计计数。
等价功能的可运行代码
下面的代码和你最初用第三方Stream库写的逻辑完全对应,只遍历一次就完成过滤、打印元素、计数的全流程,输出和原示例完全一致:
#include <iostream> #include <ranges> #include <vector> #include <string> using namespace std; int main() { vector<string> einkaufsliste = { "Bier", "Käse", "Wurst", "Salami", "Senf", "Sauerkraut" }; // 搭好惰性过滤链路:筛选首字母为S的字符串 auto s_start_view = einkaufsliste | ranges::views::filter([](const string& s) { // 用C++20 string自带的starts_with替代原来的substr比较,避免生成临时substr对象,拷贝更少 return s.starts_with('S'); }); int c = 0; // 终端触发遍历:每个元素打印出来的同时累计计数,不会重复遍历 ranges::for_each(s_start_view, [&c](const string& s) { cout << s << '\n'; c++; }); cout << c << endl; }
补充说明
如果想尽量贴近第三方库那种全链路管道拼接的写法,自己写个简单的管道适配器把终端操作包成支持|调用的形式就行,但这不是C20标准原生支持的能力。另外从C23开始,标准给大多数终端算法加了管道拼接支持,到时候就可以直接把终端操作接在视图链路后面用了。
内容的提问来源于stack exchange,提问作者Matthias
相关产品推荐
相关产品推荐

