C++的新標(biāo)準(zhǔn)又雙叒叕要到來(lái)了,是的,C++20要來(lái)了!
圖片來(lái)源:udemy.com
幾周前,C++標(biāo)準(zhǔn)委會(huì)歷史上規(guī)模最大的一次會(huì)議(180人參會(huì))在美國(guó)San Diego召開(kāi),這次的會(huì)議上討論確定哪些特性要加入到C++20中,哪些特性可能加入到C++20中。在明年二月份的會(huì)議當(dāng)中將正式確定所有的C++20特性。
這次會(huì)議討論的提案也是非常之多,達(dá)到了創(chuàng)紀(jì)錄的274份,C++20的新特性如果要一一列出的話(huà)將是一份長(zhǎng)長(zhǎng)的清單,因此本文將只評(píng)論大部分確定要加入和可能加入到C++20的重要特性,讓讀者對(duì)C++的未來(lái)和演進(jìn)趨勢(shì)有一個(gè)基本的了解。
C++20中可能增加哪些重要特性,下面這個(gè)圖可以提供一個(gè)參考。
下面是本文將評(píng)論的將進(jìn)入和可能進(jìn)入C++20的重要特性:
Concepts
Ranges
Modules
Coroutines
Reflection
接下來(lái)讓我們慢慢揭開(kāi)C++20的面紗,看看這些特性到底是什么樣的,它們解決了什么問(wèn)題。
Concepts
在談Concepts之前我想先介紹一下Concepts提出的背景和原因。眾所周知,因?yàn)镃++的模版和模版元具備非常強(qiáng)大的泛型抽象能力并且是zero overhead,所以模版在C++中備受推崇,大獲成功,在各種C++庫(kù)(如STL)中被廣泛使用。
然而,模版編程還存在一些問(wèn)題,比如有些模版的代碼寫(xiě)起來(lái)比較困難,讀起來(lái)比較難懂,尤其是編譯出錯(cuò)的時(shí)候,那些糟糕的讓人摸不著頭腦的錯(cuò)誤提示讓人頭疼。因此,C++之父Bjarne Stroustrup很早就希望對(duì)模版做一些改進(jìn),讓C++的模版編程變得簡(jiǎn)單好寫(xiě),錯(cuò)誤提示更明確。他早在1987年就開(kāi)始做這方面的嘗試了。
C++之父Bjarne Stroustrup
具體思路就是給模版參數(shù)加一些約束,這些約束相比之前的寫(xiě)法具有更強(qiáng)的表達(dá)能力和可讀性,會(huì)簡(jiǎn)化C++的泛型模版代碼的編寫(xiě)。
所以Concepts的出現(xiàn)主要是為了簡(jiǎn)化泛型編程,一個(gè)Concept就是一個(gè)編譯期判斷,用于約束模版參數(shù),Concepts則是這些編譯期判斷的合集。下面通過(guò)一個(gè)例子來(lái)展示Concepts是如何簡(jiǎn)化模版編程的。
template
比如有這樣一個(gè)類(lèi)B,我們調(diào)用它的成員函數(shù)tostring時(shí),對(duì)T類(lèi)型進(jìn)行限定,即限定T類(lèi)型是std::string的可轉(zhuǎn)換類(lèi)型,這樣做的目的是為了更安全,能在編譯期就能檢查錯(cuò)誤。這里通過(guò)C++14的std::enableif_t來(lái)對(duì)T進(jìn)行限定,但是長(zhǎng)長(zhǎng)的enableift看起來(lái)比較冗長(zhǎng)繁瑣,頭重腳輕。來(lái)看看用Concepts怎么寫(xiě)這個(gè)代碼的。
template
可以看到,requires CastableToString比之前長(zhǎng)長(zhǎng)的enableift要簡(jiǎn)潔不少,代碼可讀性也更好,CastableToString就是一個(gè)Concept,一個(gè)限定T為能被轉(zhuǎn)換為std::string類(lèi)型的Concept,通過(guò)requires相連接,語(yǔ)義上也更明確了,而且這個(gè)Concept還可以復(fù)用。
Concepts的這個(gè)語(yǔ)法也可能在最終的C++20中有少許不同,有可能還會(huì)變得更簡(jiǎn)潔,現(xiàn)在語(yǔ)法有幾個(gè)候選版本,還沒(méi)最終投票確定。
Ranges
相比STL,Ranges是更高一層的抽象,Ranges對(duì)STL做了改進(jìn),它是STL的下一代。為什么說(shuō)Ranges是STL的未來(lái)?雖然STL在C++中提供的容器和算法備受推崇和廣泛被使用,但STL一直存在兩個(gè)問(wèn)題:
STL強(qiáng)制你必須傳一個(gè)begin和end迭代器用來(lái)遍歷一個(gè)容器;
STL算法不方便組合在一起。
STL必須傳迭代器,這個(gè)迭代器僅僅是輔助你完成遍歷序列的技術(shù)細(xì)節(jié),和我們的函數(shù)功能無(wú)關(guān),大部分時(shí)候我們需要的是一個(gè)range,代表的是一個(gè)比迭代器更高層的抽象。
那么Ranges到底是什么呢?Ranges是一個(gè)引用元素序列的對(duì)象,在概念上類(lèi)似于一對(duì)迭代器。這意味著所有的STL容器都是Ranges。在Ranges里我們不再傳迭代器了,而是傳range。比如下面的代碼:
STL寫(xiě)法:
std::vector
Ranges寫(xiě)法:
std::sort(v);
STL有時(shí)候不方便將一些算法組合在一起,來(lái)看一個(gè)例子:
std::vector
上面這個(gè)例子希望得到vector中的偶數(shù)乘以2的結(jié)果,需求很簡(jiǎn)單,但是用STL寫(xiě)起來(lái)還是有些冗長(zhǎng)繁瑣,中間還定義了兩個(gè)臨時(shí)變量。如果用Ranges來(lái)實(shí)現(xiàn)這個(gè)需求,代碼就會(huì)簡(jiǎn)單得多。
autoresults=v|ranges::view::filter([](inti){returni%2==0;})|ranges::view::transform([](inti){returni*2;});
用Concetps我們可以很方便地將算法組合在一起,寫(xiě)法更簡(jiǎn)單,語(yǔ)義更清晰,并且還可以實(shí)現(xiàn)延遲計(jì)算避免了中間的臨時(shí)變量,性能也會(huì)更好。
Concepts從設(shè)計(jì)上改進(jìn)了之前STL的兩個(gè)問(wèn)題,讓我們的容器和算法變得更加簡(jiǎn)單好用,還容易組合。
Modules
一直以來(lái)C++一直通過(guò)引用頭文件方式使用庫(kù),而其他90年代以后的語(yǔ)言比如Java、C#、Go等語(yǔ)言都是通過(guò)import包的方式來(lái)使用庫(kù)?,F(xiàn)在C++決定改變這種情況了,在C++20中將引入Modules,它和Java、Go等語(yǔ)言的包的概念是類(lèi)似的,直接通過(guò)import包來(lái)使用庫(kù),再也看不到頭文件了。
為什么C++20不再希望使用#include方式了?因?yàn)槭褂妙^文件方式存在不少問(wèn)題,比如有include很多模版的頭文件將大大增加編譯時(shí)間,代碼生成物也會(huì)變大。而且引用頭文件方式不利于做一些C++庫(kù)和組件的管理工具,尤其是對(duì)于一些云環(huán)境和分布式環(huán)境下不方便管理,C++一直缺一個(gè)包管理工具,這也是C++被吐槽得很多的地方,現(xiàn)在C++20 Modules將改變這一切。
Modules在程序中的結(jié)構(gòu)如下圖:
上面的圖中,每個(gè)方框表示一個(gè)翻譯單元,存放在一個(gè)文件里并且可以被獨(dú)立編譯。每個(gè)Module由Module接口和實(shí)現(xiàn)組成,接口只有一份,實(shí)現(xiàn)可以有多份。
Modules接口和實(shí)現(xiàn)的語(yǔ)法:
exportmodulemodule_name;modulemodule_name;
使用Modules:
importmodule_name;
Modules允許你導(dǎo)出類(lèi),函數(shù),變量,常量和模版等等。
接下來(lái)看一個(gè)使用Modules的例子:
importstd.vector;//#include
可以看到不用再include了,直接去import需要用到的Modules即可,是不是有種似曾相識(shí)的感覺(jué)呢。曾看到一個(gè)人說(shuō)如果C++支持了Modules他就會(huì)從Java回歸到C++,也說(shuō)明這個(gè)特性也是非常受關(guān)注和期待的。
Coroutines
很多語(yǔ)言提供了Coroutine機(jī)制,因?yàn)镃oroutine可以大大簡(jiǎn)化異步網(wǎng)絡(luò)程序的編寫(xiě),現(xiàn)在C++20中也要加入?yún)f(xié)程了(樂(lè)觀估計(jì)C++20加入,悲觀估計(jì)在C++23中加入)。
如果不用協(xié)程,寫(xiě)一個(gè)異步的網(wǎng)絡(luò)程序是不那么容易的,以boost.asio的異步網(wǎng)絡(luò)編程為例,我們需要注意的地方很多,比如異步事件完成的回調(diào)函數(shù)中需要保證調(diào)用對(duì)象仍然存在,如何構(gòu)建異步回調(diào)鏈條等等,代碼比較復(fù)雜,而且出了問(wèn)題也不容易調(diào)試。而協(xié)程給我們提供了對(duì)異步編程優(yōu)雅而高效的抽象,讓異步編程變得簡(jiǎn)單!
C++ Courotines中增加了三個(gè)新的關(guān)鍵字:co_await,co_yield和co_return,如果一個(gè)函數(shù)體中有這三個(gè)關(guān)鍵字之一就變成Coroutine了。
co_await用來(lái)掛起和恢復(fù)一個(gè)協(xié)程,co_return用來(lái)返回協(xié)程的結(jié)果,co_yield返回一個(gè)值并且掛起協(xié)程。
下面來(lái)看看如何使用它們。
寫(xiě)一個(gè)lazy sequence:
generator
上面的例子每次調(diào)用get_integers,只返回一個(gè)整數(shù),然后協(xié)程掛起,下次調(diào)用再返回一個(gè)整數(shù),因此這個(gè)序列不是即時(shí)生成的,而是延遲生成的。
接下來(lái)再看一下co_wait是如何簡(jiǎn)化異步網(wǎng)絡(luò)程序的編寫(xiě)的:
chardata[1024];for(;;){std::size_tn=co_awaitsocket.async_read_some(boost::asio::buffer(data),token);co_awaitasync_write(socket,boost::asio::buffer(data,n),token);}
這個(gè)例子僅僅用了四行代碼就完成了異步的echo,非常簡(jiǎn)潔!co_await會(huì)在異步讀完成之前掛起協(xié)程,在異步完成之后恢復(fù)協(xié)程繼續(xù)執(zhí)行,執(zhí)行到async_write時(shí)又會(huì)掛起協(xié)程直到異步寫(xiě)完成,異步寫(xiě)完成之后繼續(xù)異步讀,如此循環(huán)。如果不用協(xié)程代碼會(huì)比較繁瑣,需要像這樣寫(xiě):
voiddo_read(){autoself(shared_from_this());socket_.async_read_some(boost::asio::buffer(data_,max_length),[this,self](boost::system::error_codeec,std::size_tlength){if(!ec){do_write(length);}});}voiddo_write(std::size_tlength){autoself(shared_from_this());boost::asio::async_write(socket_,boost::asio::buffer(data_,length),[this,self](boost::system::error_codeec,std::size_t/*length*/){if(!ec){do_read();}});}
可以看到,不使用協(xié)程來(lái)寫(xiě)異步代碼的話(huà),需要構(gòu)建異步的回調(diào)鏈,需要保持異步回調(diào)的安全性等等。而使用協(xié)程可以大大簡(jiǎn)化異步網(wǎng)絡(luò)程序的編寫(xiě)。
Reflection
C++中一直缺少反射功能,其他很多語(yǔ)言如Java、C#都具備運(yùn)行期反射功能。反射可以用來(lái)做很多事情:比如做對(duì)象的序列化,把對(duì)象序列化為JSON、XML等格式,以及ORM中的實(shí)體映射,還有RPC遠(yuǎn)程過(guò)程(方法)調(diào)用等,反射是應(yīng)用程序中非常需要的基礎(chǔ)功能。現(xiàn)在C++終于要提供反射功能了,C++20中可會(huì)將反射作為實(shí)驗(yàn)庫(kù),在C++23中正式加入到標(biāo)準(zhǔn)中。
在反射還沒(méi)有進(jìn)入到C++標(biāo)準(zhǔn)之前,有很多人做了一些編譯期反射的庫(kù),比如purecpp社區(qū)開(kāi)源的序列化引擎iguana,以及ORM庫(kù)ormpp,都是基于編譯期反射實(shí)現(xiàn)的。然后,非語(yǔ)言層面支持的反射庫(kù)存在種種不足之處,比如在實(shí)現(xiàn)上需要大量使用模版元和宏、不能訪(fǎng)問(wèn)私有成員等問(wèn)題。
現(xiàn)在C++終于要提供完備地編譯期反射功能了,為什么是編譯期反射而不是像其它語(yǔ)言一樣提供運(yùn)行期反射,因?yàn)镃++的一個(gè)重要設(shè)計(jì)哲學(xué)就是zero-overhead,編譯期反射效率遠(yuǎn)高于運(yùn)行期反射。
那么,通過(guò)C++20的編譯期反射我們能得到什么呢?我們可以得到很多很多關(guān)于類(lèi)型和對(duì)象的元信息,主要有:
獲取對(duì)象類(lèi)型或枚舉類(lèi)型的成員變量,成員函數(shù)的類(lèi)型;
獲取類(lèi)型和成員的名稱(chēng);
獲取成員變量是靜態(tài)的還是constexpr;
獲取方法是virtual、public、protect還是private;
獲取類(lèi)型定義時(shí)的源代碼所在的行和列。
所以C++20的反射其實(shí)是提供了一些可以編譯期向編譯器查詢(xún)目標(biāo)類(lèi)型“元數(shù)據(jù)”的API,下面來(lái)看看C++20的反射用法:
structperson{intid;std::stringname;};usingMetaPerson=reflexpr(person);usingMembers=std::reflect::get_data_members_t
上面的例子中,C++20新增關(guān)鍵字reflexpr返回的是person的元數(shù)據(jù)類(lèi)型,接下來(lái)我們就可以查詢(xún)這個(gè)元數(shù)據(jù)類(lèi)型了,std::reflect::getdatamembers_t返回的是對(duì)象成員的元數(shù)據(jù)序列,我們可以像訪(fǎng)問(wèn)tuple一樣訪(fǎng)問(wèn)這個(gè)序列,得到某一個(gè)字段的元數(shù)據(jù)之后我們就可以獲取它的具體信息了,比如它的具體類(lèi)型是什么,它的字段名是什么,它是公有還是私有的等等。
注意:C++20的反射語(yǔ)法還沒(méi)有最終確定,這只是一種候選的語(yǔ)法實(shí)現(xiàn),還有一種沒(méi)有元編程的語(yǔ)法版本,該版本通過(guò)編譯期容器和字符串來(lái)存放元數(shù)據(jù),比如constexpr std::vector,constexpr std::map,constexprstd::string等 ,這樣就可以像普通的C++程序那樣來(lái)操作元數(shù)據(jù)了,用起來(lái)可能更簡(jiǎn)單。
C++20的編譯期反射實(shí)際上提供了一些編譯期查詢(xún)AST信息的接口,功能完備而強(qiáng)大。
總結(jié)
Concepts讓C++的模版程序的編寫(xiě)變得更簡(jiǎn)單和容易理解;
Ranges讓我們使用STL容器和算法更加簡(jiǎn)單,并且更容易組合算法及延遲計(jì)算;
Modules幫助我們大大加快編譯速度,同時(shí)彌補(bǔ)了C++使用庫(kù)和缺乏包管理的缺陷;
Coroutines幫助我們簡(jiǎn)化異步程序的編寫(xiě);
Reflection給我們提供強(qiáng)大的編譯期AST元數(shù)據(jù)查詢(xún)能力;
......
關(guān)于C++20的更多細(xì)節(jié)讀者可以在這里查看:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/。
總而言之,C++的新標(biāo)準(zhǔn)都是為了讓C++變得更簡(jiǎn)單、更完善、更強(qiáng)大、更易學(xué)和使用,這也是C++之父希望未來(lái)C++演進(jìn)的一個(gè)方向和目標(biāo)。
C++20,一言以蔽之:Newer is Better!
在此呼吁現(xiàn)在仍然還在使用著20年前的標(biāo)準(zhǔn)C++98的公司盡早升級(jí)到最新的標(biāo)準(zhǔn),跟上時(shí)代的發(fā)展,新標(biāo)準(zhǔn)意味這生產(chǎn)力和質(zhì)量的提升,越早使用越早享受其帶來(lái)的好處!
-
編程
+關(guān)注
關(guān)注
88文章
3687瀏覽量
95072 -
C++
+關(guān)注
關(guān)注
22文章
2119瀏覽量
75035 -
代碼
+關(guān)注
關(guān)注
30文章
4895瀏覽量
70496
原文標(biāo)題:C++20 準(zhǔn)備來(lái)了,看看都有哪些新特性?
文章出處:【微信號(hào):mcuworld,微信公眾號(hào):嵌入式資訊精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
經(jīng)過(guò)一定時(shí)間將數(shù)組中的值置0
STM32CubeIDE不支持C++20語(yǔ)法嗎?
c++標(biāo)準(zhǔn)庫(kù)手冊(cè)
2020年底將正式發(fā)布C++20
關(guān)于C++ 20協(xié)程最全面詳解

蘋(píng)果iPhone因宣傳又雙叒叕被告了
現(xiàn)代C++20實(shí)戰(zhàn)手冊(cè)
C++20無(wú)棧協(xié)程超輕量高性能異步庫(kù)開(kāi)發(fā)實(shí)戰(zhàn)
C++簡(jiǎn)史:C++是如何開(kāi)始的

評(píng)論