所有C ++ 20核心语言功能

2021-04-07 21:17:45

这篇文章背后的故事很简单,我想了解新的C ++ 20个语作功能,并在单个页面上为所有这些都有一个简要的摘要。所以我决定阅读所有提案并创建这个“骗局” Thatexplains并展示了每个功能。这不是“最畅销书”的文章,它只适用于实证的目的。大多数例子是激励或直接从相应的建议中获取,所有信贷都会向其作者和ISO C ++委员会成员的工作。享受!

概念背后的基本想法是指定Templateargument所需的内容,以便编译器可以在实例化之前检查。因此,如果有的话,错误消息,如果有的话,则不满足约束X.Before C ++ 20,可以使用棘手的enable_if结构,或者在模板实例化与密码错误消息期间失败。概念失败早期发生,错误消息很干净。

让我们从需要表达式开始。它是一个表达式,其中包含模板参数的实际要求,如果满足它们,则评估为true,否则为false。

模板< typename t> /* ...*/requires(t x)// fictional参数的可选集{//简单要求:表达式必须是有效的x ++; //表达式必须有效//类型要求:`typeName t`,t键入必须是有效的类型typeename t :: value_type;类型的s< t&gt ;; //化合物要求:{表达式} [noexcept] [ - >概念]; // {表达式} - >概念< a1,a2,...>相当于//需要概念< decltype((表达式)),a1,a2,...> {*X}; // dereference必须有效{* x} noexcept; // dereference必须是noexcept // dereference必须返回t :: value_type {* x} noexcept - > std :: scal_as< typename t :: value_type&gt ;; //嵌套要求:需要ConceptName< ...&gt ;;需要可添加< t&gt ;; //约束可添加< t>必须满足};

概念只是一种命名的此类约束或其逻辑组合。概念,并且需要表达式呈现为编译时bool值,并且可以用作正常值,例如在if constexpr中。

模板< typename t>概念可添加=需要(t a,t b){a + b;};模板< typename t>概念分离=需要(t a,t b){a / b;};模板< typename t&gt ;概念divaddable =可添加< t> &&可分离的< t&gt ;;模板< typename t> void f(t x){如果constexpr(可添加< t>){/*...*/}如果constexpr(需要(t a,t b){a + b ;}){/*...*/}}

实际上约束我们需要的东西 - 条款。它可能出现右后模板<块或作为函数声明的最后一个元素,一次oreven一次,lambdas包括:

模板< typeName t>需要可添加的< t>自动f1(t a,t b)需要可减去可减的< t> //可添加< t> &&可减去可减轻< t> auto l = []< typename t>需要可添加< t> (t a,t b)需要可减去可减的< t> {};模板< typename t>需要可添加的< t> c; //臭名昭着的`需要`。第一个`需要`需要 - 子句,//第二个是需要表达式。如果您不想介绍新的//概念.Template< typeName t>需要(t a,t b){a + b;}自动f4(t x);

更清晰的方式是使用概念名称而不是类/ typename关键字Intemplate参数列表:

模板模板参数也可以限制。在这种情况下,参数必须少于或同样受限于参数。不受约束的模板模板参数仍然可以接受约束模板作为参数:

模板< typename t>概念积分= std ::积分< t&gt ;;模板< typename t>概念积分4 = std ::积分< t> && sizeof(t)== 4; //需要-trese也适用于Heretemplate<模板< typeName t1>需要积分< t1> TypeName T≫ void F2(){} // f()和f2()表单是平底板<模板<积分t1> TypeName T≫ void f(){f2< t>();} //未经控制的模板模板参数可以接受受限的Argumentstemplate<模板< typename t1> TypeName T> void F3()模板< typename t> struct s s s1 {};模板<结构s s s2 {};模板<积分4 t> struct s s3 {}; void test(){f h& lt; s1>() ; // OK F< s2>(); // OK //错误,S3受限于积分4,其比// f()' s积分f>(); //全部是OK F3< s1>(); F3< s2>(); F3< s3>();}

模板< typename t> struct x {void f()需要std ::积分< t> {}}; void f(){x< double> X; x.f(); //错误自动pf =& x<双倍> :: f; // 错误}

自动参数现在允许正常函数使它们刚刚刚刚像genGENERIC lambdas。概念可用于在各种上下文中约束占位符类型(自动/ DECLTYPE(AUTO))。对于参数包,MyConcept ... TS需要MyConcept对于包装的每个元素,而不是全部包装,例如整个包装。需要< t1> &&需要< t2> && ...&&需要< tlast&gt ;.

模板&lt; typename t&gt;概念是_sortable = true; auto l = [](auto x){}; void f1(auto x){} //不受约束的templatevoid f2(is_sortable auto x){} //约束templateTemplate&lt; is_sortable auto portpeplameter ,is_sortable typeparameter&gt; is_sortable自动f3(is_sortable auto x,auto y){//注意,约束名称和`auto`is_sortable自动z = 0之间允许什么都不允许;返回0;模板&lt; is_sortable auto ... nontypepack,is_sortable ... typepack&gt; void f4(typepack ... args){int f(); //需要两个parameterstemplate&lt; typename t1,typename t2&gt;概念c = true; //绑定第二个parameterc&lt; double&gt;自动v = f(); //表示C&lt; int,double&gt; struct x {operator is_sortable auto(){return 0; }};自动f5() - &gt; IS_SOSTABLE DECLTTYPE(AUTO){F4 <1,2,3&gt;(1,2,3);返回新的IS_SORTABLE AUTO(1);}

Warning: Can only detect less than 5000 characters

模块是将C ++代码组织成逻辑组件的新方法。从历史上看,C ++使用的C型号基于预处理器和重复的文本包容。它有很多问题,如宏泄漏进出标题,包含秩序依赖者,重复汇编相同代码,循环依赖项,封装封装不良的实施细节等。模块即将解决它们不那么快。直到编译器和Build工具(如CMake)也会支持它,我们将无法使用它们的全部电源。模块的完整描述超出了本文的范围,我将仅显示基本思路和用例。有关更多详细信息,您可以通过向量 - Boolor Vectory-offorgor读取一系列文章,只谷歌为其他博客帖子或会谈。

模块背后的主要思想是在其客户端使用(导入)时限制可访问(导出)的内容。这允许真实地隐藏实施细节。

// module.cpp //模块名称中的点为可读性目的,它们没有特殊的indexport模块my.tool; //模块声明void f(){} //导出f()void g(){} //但不是g()// client.cppimport my.tool; f(); // okg(); //错误,不导出

模块是宏观不友好的,您将手动#defined宏传递给模块(编译器的内置和密码线宏仍然可见),只能在一个特殊情况下,您可以从蒙显示器导入macros。模块不能具有循环依赖项。模块是一种自色,编译器可以完全一旦整体编译时间得到大大提高。 importOritor对模块无关紧要。

模块可以是接口或实现模块单元。只有InterfaceUnits可以贡献模块的界面,这就是为什么他们有导出IntheIrdecraration的原因。模块可以是单个文件或跨分区分散。每个部分以form module_name:partition_name命名。分区仅在同一模块中可进入同一模块,客户端只能导入整个模块。这提供了比标题文件更好的封装。

// tool.cppexport模块工具; //主模块接口UniTexport导入:助手; //重新出口(见下文)帮助程序PartitionExport void f();导出void g(); //工具.internals.cppmodule工具:内部; //实现partitionvoid实用程序(); // tool.impl.cppmodule工具; //实现单位,隐式导入主模块Unitimport:内部; void实用程序(){} void f(){utility();} // tool.impl2.cppmodule工具; //另一个实现UnitVoid g(){} //工具.helpers.cppexport模块工具:助手; //模块界面partitionImport:内部;导出void h(){utility();} // client.cppimport工具; f(); g(); h();

请注意,未在不指定模块名称的情况下导入分区。这禁止进入其他模块的分区。允许多个实现单元(模块工具;),所有其他单位和任何类型的单位和分区都是独一无二的。必须通过导出导入由模块重新导出所有接口分区。

以下是各种形式的导出,一般规则是您无法使用内部链接导出:

// tool.cppmodule工具;导出导入:助手; //导入和重新出口求助者接口PartitionExport Int x {}; //导出单个声明{//导出多个声明int y {}; void f(){};}导出命名空间a {//导出整个命名空间void f(); void g();命名空间b {导出void f(); //在命名空间void g()中导出单个声明; namespace {export int x; //错误,x有内部链接导出void f(); //错误,f()具有内部链接}导出class c; //导出为不完整的typeclass c {};导出c get_c(); // client.cppimport工具; c c1; //错误,c是incompleteauto c2 = get_c(); // 好的

导入声明应在任何其他“非模块”声明之前,这是快速依赖性分析。否则,它非常直观:

// tool.cppexport模块工具;导入:佣工; //导入everversexport void f(){} // tool.helpers.cppexport模块工具:eververs; void g(){} // client.cppimport工具; f(); g();

有一种特殊的导入表单,允许导入可进口头:导入&lt; header.h&gt;或导入&#34;标题.h&#34;编译器创建合成的标题单元,并使所有隐式导出的声明。作为实际的标题是进口实现定义的,但所有C ++库标题都是如此。也许,将有一种方法可以告诉编译器,用户提供的标题是可进度的,这些标题不应包含非内联函数定义或变量,其中包含无外的链接。这是允许从标题导入宏的唯一导入表单(但您仍然无法通过导出导入和#34重新导出它们;标题.H&#34;)。如果您不确定其内容,请勿使用它来导入OrdonLegacy标头。

如果您需要在模块中使用旧学校标题,则会安全地放置一个特殊的Placito:全球模块片段:

// header.h#pragma onceclass a {}; void g(){} // tool.cppmodule; //全局模块片段#include&#34; header.h&#34;出口模块工具; //结束thereexport void f(){//使用header.h g()的声明; A;}

它必须在命名模块声明之前出现,它只能包含预处理ordirectives。所有全局模块片段和非模块化转移单元的所有声明都附加到单个全局模块。因此,普通标题的所有规则都适用于此。

最终的奇怪野兽是一个私有模块片段。它的意图是在单个文件模块中删除详细信息(它不允许在其他地方)。从理论上讲,当私有模块片段中的事情发生变化时,客户端可能会重新编译:

导出模块工具; // InterfaceExport void F(); //宣布了Heremodule:私人; //实现详细信息void f(){} //在此定义

内联也有一个有趣的变化。如果该类连接在命名模块上,则SecordedUTHININITHIN的成员函数不会内联无内联。命名模块中的内联函数只能使用客户端可见Namesthat。

// header.hstruct c {void f(){} //仍然是内联的,因为附加到全局模块}; // tool.cppmodule; #include&#34; header.h&#34;出口模块工具; A类{ }; //不是导出的Exportstruct b {// b附加到模块&#34;工具&#34; void f(){//未隐式内联A一个; //可以安全地使用非导出名称}内联void g(){a; // oops,使用非导出名称}内联void h(){f(); // fine,f()不是内联}}; // client.cppimport工具; b; b.f(); // okb.g(); //错误,a是undefinedb.h(); // 好的

最后,我们有堆积(他们的状态在C ++中存储在堆中)coroutines。 C ++ 20提供了最低可能的API和叶子休息到User.weve GOT CO_AWAIT,CO_YIELD,CO_RETURN关键字和COLLERAND CALLEE之间的交互规则。这些规则是如此低的级别,我认为在解释主题方面没有任何意见。你可以在Lewis Baker的博客上找到更多细节.Hopely,C ++ 23将填补一些库实用程序的差距。在此之前,我们可以使用第三方库,这是一个使用cppcoro的示例:

cppcoro ::任务&lt; int&gt; someasynctask(){int结果; //获得结果以某种方式c​​o_return结果;} //任务&lt;&gt;是普通功能的空隙的模拟pppcoro ::任务&lt;&gt; UsageExample(){//创建一个新任务,但&#39; t开始执行coroutine但cppcoro ::任务&lt; int&gt; mytask = someasynctask(); // ... // coroutine只在我们以后的co_await任务时开始。自动结果= co_await mytask;} //将懒惰地生成0到9cppcoro :: generator&lt; std :: size_t&gt; gettennumbers(){std :: size_t n {0};而(n!= 10){co_yield n ++; void printnumbers(){for(for(const auto n:gettennumbers()){std :: cout

在C ++ 20之前,为类提供比较操作,需要6个操作员的实现:==,!=,&lt; =,&gt; =。通常,其中四个含有锅炉板 根据== a工作的代码 ......