C++20新特性精讲:概念、范围与三路比较
本文是 C 系列教程的第 22 篇。上一篇讲解了 C17 新特性本篇深入 C20 新特性概念concepts、范围ranges、三路比较spaceship、协程与模块等覆盖 8 个完整示例代码。一、概念Concepts1.1 为什么需要概念C20 之前模板参数的约束只能依赖文档和编译期报错。概念Concept把「类型必须满足的约束」变成一等公民让编译器给出清晰错误也让重载决议更精确#includeiostream#includeconcepts#includestringusingnamespacestd;// 定义概念类型必须可相加且结果可转换为目标类型templatetypenameT,typenameUconceptAddablerequires(T a,U b){{ab}-convertible_toT;};// 使用概念约束模板参数templateAddable TTadd(T a,T b){returnab;}intmain(){coutadd(3,4)endl;// 7int 满足 Addablecoutadd(1.5,2.5)endl;// 4double 满足// cout add(string(a), 1); // 编译错误stringint 不可相加return0;}概念由concept儳央字声明requires表达式描述约束。约束失败时编译器会指出「不满足哪个概念」而不是给出深奥的模板实例化错误。1.2 标准概念库concepts头文件提供大量现成概念integral、floating_point、signed_integral、same_as、convertible_to、derived_from、assignable_from等#includeiostream#includeconcepts#includetype_traitsusingnamespacestd;// 限定参数必须是整型templateintegral TTtwice(T x){returnx*2;}// 多个约束组合合取templateintegral Trequires(sizeof(T)4)voidprintWide(T x){coutx 至少4字节endl;}intmain(){couttwice(21)endl;// 42int 是整型// cout twice(3.14) endl; // 错误double 不是 integralprintWide(100);// 满足 sizeof(int)4return0;}约束可以写在template...后直接附加如template integral T也可以用requires子句补充更复杂条件。二、范围Ranges2.1 管道运算符初体验ranges引入惰性求值的视图View配合管道运算符|让数据处理像流水一样清晰。视图不拷贝元素按需计算#includeiostream#includeranges#includevector#includenumericusingnamespacestd;intmain(){vectorintv(10);iota(v.begin(),v.end(),1);// 1..10// 取偶数 - 平方 - 只取前3个一气呵成autoresultv|views::filter([](intn){returnn%20;})// 2 4 6 8 10|views::transform([](intn){returnn*n;})// 4 16 36 64 100|views::take(3);// 4 16 36for(intx:result)coutx ;// 4 16 36coutendl;return0;}views::filter与views::transform返回惰性视图只有遍历时才真正计算且不产生临时容器性能优于手写中间容器。2.2 ranges 算法与投影std::ranges命名空间重新实现了全部 STL 算法支持直接传容器、支持投影projection和哨兵sentinel#includeiostream#includeranges#includevector#includealgorithm#includestringusingnamespacestd;structStudent{string name;intscore;};intmain(){vectorStudentstudents{{张三,85},{李四,92},{王五,78},{赵六,95}};// 直接传容器旧版要 begin/end投影到 score 排序ranges::sort(students,ranges::greater{},Student::score);for(autos:students){couts.name: s.scoreendl;}// 查找最高分autoitranges::max_element(students,{},Student::score);cout最高分: it-name it-scoreendl;return0;}投影Student::score让算法在排序/比较时只看该成员代码更简洁、意图更明确。三、三路比较Spaceship3.1 飞船操作符 三路比较操作符一次比较即可得到「小于 / 等于 / 大于」三种结果返回strong_ordering强序或partial_ordering弱序允许 NaN 等#includeiostream#includecompareusingnamespacestd;intmain(){autor35;if(r0)cout3 5endl;elseif(r0)cout3 5endl;elsecout3 5endl;// 返回类型couttypeid(decltype(35)).name()endl;// strong_orderingcouttypeid(decltype(3.05.0)).name()endl;// partial_orderingreturn0;}3.2 默认三路比较自动生成全套运算符只要重载operator并声明为 default编译器自动生成、、、!、、全部 6 个运算符省去大量样板代码#includeiostream#includecompare#includevector#includealgorithmusingnamespacestd;structPoint{intx,y;// 按 x 再按 y 自动比较autooperator(constPoint)constdefault;};intmain(){vectorPointpts{{3,1},{1,9},{2,5}};sort(pts.begin(),pts.end());// 直接可用 排序for(autop:pts)cout(p.x,p.y) ;coutendl;Point a{1,2},b{1,3};cout(ab)endl;// 1x 相等比 ycout(a!b)endl;// 1return0;}成员按声明顺序逐项比较字典序。 default的要求所有成员都支持或自带比较运算符。四、协程与模块C20 重要特性4.1 协程co_await 与生成器C20 协程让「可暂停/可恢复」的函数成为语言特性。虽然标准库生成器要等 C23但可以借助std::generator的替代品或自建极简协程。以下用 C20 编译的最小协程示例需要协程支持库#includeiostream#includecoroutineusingnamespacestd;// 极简 generator每次 co_yield 产生一个整数structGenerator{structpromise_type{intcurrent;Generatorget_return_object(){returnGenerator{this};}suspend_alwaysinitial_suspend(){return{};}suspend_alwaysfinal_suspend()noexcept{return{};}suspend_alwaysyield_value(intv){currentv;return{};}voidreturn_void(){}voidunhandled_exception(){}};usinghandlecoroutine_handlepromise_type;handle h;explicitGenerator(promise_type*p):h(handle::from_promise(*p)){}~Generator(){if(h)h.destroy();}boolnext(){h.resume();return!h.done();}intvalue(){returnh.promise().current;}};Generatorcounter(intn){for(inti1;in;i)co_yieldi;// 挂起点}intmain(){autogencounter(5);while(gen.next())coutgen.value() ;// 1 2 3 4 5coutend l;return0;}协程调用时并不立即执行首次resume()才运行到第一个挂起点。co_yield把值交给调用方后挂起下次resume()继续。4.2 模块Modules初探模块取代头文件成为新的代码组织方式编译更快、隔离更好。模块文件通常以.cppm结尾// math.cppm —— 模块接口文件exportmodulemath;exportintsquare(intx){returnx*x;}exportdoublepi(){return3.14159265358979;}// main.cpp —— 使用模块importiostream;importmath;intmain(){std::coutsquare(7)std::endl;// 49std::coutpi()std::endl;// 3.14159return0;}注意模块编译需要较新的编译器GCC 11、Clang 16、MSVC 2019 16.10且部分构建系统如 CMake 3.28才完整支持。五、其他实用新特性5.1 std::span零开销数组视图std::span是连续内存的「视图」不拥有数据可直接当数组使用避免指针长度参数#includeiostream#includespan#includevectorusingnamespacestd;voidprintAll(spanconstints){for(intx:s)coutx ;coutendl;}intmain(){intarr[]{1,2,3,4,5};vectorintvec{10,20,30};printAll(arr);// 数组自动转为 spanprintAll(vec);// vector 自动转为 spanprintAll({arr1,3});// 指定子区间 2 3 4return0;}span不拷贝元素、不管理内存是函数充参的最佳实践之一。5.2 constexpr 扩展与立即函数C20 允许constexpr函数使用try、new/delete、虚函数等还引入consteval立即函数只能在编译期调用#includeiostream#includevectorusingnamespacestd;constevalintsquare(intx){// 立即函数只允许编译期求值returnx*x;}constexprintfactorial(intn){if(n1)return1;returnn*factorial(n-1);}intmain(){constexprintasquare(9);// 编译期计算 81constexprintffactorial(6);// 720couta fendl;return0;}consteval函数若在运行时上下文调用会直接编译错误保证所有调用都发生在编译期。六、实战C20 成绩分析器综合运用概念、范围、三路比较与 span 的完整案例#includeiostream#includeranges#includevector#includealgorithm#includeconcepts#includecompare#includespan#includestringusingnamespacestd;// 概念成绩必须是算术类型templatetypenameTconceptScorearithmeticT;structStudent{string name;Scoreautoscore;// 概念简写约束成员类型autooperator(constStudent)constdefault;// 自动比较};// 用 span 接收成绩数组templateScore Tdoubleaverage(spanconstTscores){doublesum0;for(autos:scores)sums;returnscores.empty()?0:sum/scores.size();}intmain(){vectorStudentstudents{{张三,88},{李四,92.5},{王五,76},{赵六,95}};// 范围算法 投影按分数降序ranges::sort(students,ranges::greater{},Student::score);cout按分数排序:endl;for(autos:students)couts.name s.scoreendl;// 取前两名视图 takeautotop2students|views::take(2);cout前两名: ;for(autos:top2)couts.name ;coutendl;// 计算平均分span 传入vectordoublescores;for(autos:students)scores.push_back(s.score);cout平均分: average(spanconstdouble(scores))endl;return0;}总结本篇系统讲解了 C20 核心新特性concepts让模板约束可读、可复用ranges以惰性视图与管道风格重构数据流三路比较一次操作自动生成全套比较运算符协程与模块开启语言新范式std::span与constexpr扩展让代码更安全高效。建议在 GCC 11/Clang 16/MSVC 2019 16.10 上开启-stdc20实测。下一篇将进入C 并发编程线程与互斥锁敬请期待
上一篇/下一篇内容由系统自动关联
返回资讯列表 →