在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C++23特性概覽

CPP開發(fā)者 ? 來源:CPP開發(fā)者 ? 2023-02-02 10:28 ? 次閱讀

新年伊始,要說什么選題最合適,那無疑是C++23了。

23是個(gè)小版本,主要在于「完善」二字,而非「新增」。因此,值得單獨(dú)拿出來寫篇文章的特性其實(shí)并不多,大多特性都是些瑣碎小點(diǎn),三言兩語便可講清。

本篇包含絕大多數(shù)C++23特性,難度三星就表示只會(huì)介紹基本用法,但有些特性的原理也會(huì)深入講講。

1Deducing this(P0847)

Deducing this是C++23中最主要的特性之一。msvc在去年3月份就已支持該特性,可以在v19.32之后的版本使用。

為什么我們需要這個(gè)特性?

大家知道,成員函數(shù)都有一個(gè)隱式對象參數(shù),對于非靜態(tài)成員函數(shù),這個(gè)隱式對象參數(shù)就是this指針;而對于靜態(tài)成員函數(shù),這個(gè)隱式對象參數(shù)被定義為可以匹配任何參數(shù),這僅僅是為了保證重載決議可以正常運(yùn)行。

Deducing this所做的事就是提供一種將非靜態(tài)成員函數(shù)的「隱式對象參數(shù)」變?yōu)椤革@式對象參數(shù)」的方式。為何只針對非靜態(tài)成員函數(shù)呢?因?yàn)殪o態(tài)成員函數(shù)并沒有this指針,隱式對象參數(shù)并不能和this指針劃等號(hào),靜態(tài)函數(shù)擁有隱式對象參數(shù)只是保證重載決議能夠正常運(yùn)行而已,這個(gè)參數(shù)沒有其他用處。

于是,現(xiàn)在便有兩種寫法編寫非靜態(tài)成員函數(shù)。

1structS_implicit{
2voidfoo(){}
3};
4
5structS_explicit{
6voidfoo(thisS_explicit&){}
7};

通過Deducing this,可以將隱式對象參數(shù)顯式地寫出來,語法為this+type。

該提案最根本的動(dòng)機(jī)是消除成員函數(shù)修飾所帶來的冗余,舉個(gè)例子:

 1//Before
 2structS_implicit{
 3intdata_;
 4
 5int&foo()&{returndata_;}
 6constint&foo()const&{returndata_;}
 7};
 8
 9//After
10structS_explicit{
11intdata_;
12
13template
14auto&&foo(thisSelf&self){
15returnstd::forward(self).data_;
16}
17};
原本你也許得為同一個(gè)成員函數(shù)編寫各種版本的修飾,比如&, const&, &&, const &&,其邏輯并無太大變化,完全是重復(fù)的機(jī)械式操作。如今借助Deducing this,你只需編寫一個(gè)版本即可。 這里使用了模板形式的參數(shù),通常來說,建議是使用Self作為顯式對象參數(shù)的名稱,顧名思義的同時(shí)又能和其他語言保持一致性。 該特性還有許多使用場景,同時(shí)也是一種新的定制點(diǎn)表示方式。 比如,借助Deducing this,可以實(shí)現(xiàn)遞歸Lambdas
1intmain(){
2autogcd=[](thisautoself,inta,intb)->int{
3returnb==0?a:self(b,a%b);
4};
5
6std::cout<
這使得Lambda函數(shù)再次得到增強(qiáng)。 又比如,借助Deducing this,可以簡化CRTP
 1////Before
 2//CRTP
 3template
 4structBase{
 5voidfoo(){
 6auto&self=*static_cast(this);
 7self.bar();
 8}
 9};
10
11structDerived:Base{
12voidbar()const{
13std::cout<
22voidfoo(thisSelf&self){
23self.bar();
24}
25};
26
27structDerived:Base{
28voidbar()const{
29std::cout<
這種新的方式實(shí)現(xiàn)CRTP,可以省去CR,甚至是T,要更加自然,更加清晰。 這也是一種新的定制點(diǎn)方式,稍微舉個(gè)簡單點(diǎn)的例子:
 1//Library
 2namespacemylib{
 3
 4structS{
 5autoabstract_interface(thisauto&self,intparam){
 6self.concrete_algo1(self.concrete_algo2(param));
 7}
 8};
 9}//namespacemylib
10
11namespaceuserspace{
12structM:mylib::S{
13autoconcrete_algo1(intval){}
14autoconcrete_algo2(intval)const{
15returnval*6;
16}
17};
18}//namespaceuserspace
19
20intmain(){
21usinguserspace::M;
22Mm;
23m.abstract_interface(4);
24}
這種方式依舊屬于靜態(tài)多態(tài)的方式,但代碼更加清晰、無侵入,并支持顯式opt-in,是一種值得使用的方式。 定制點(diǎn)并非一個(gè)簡單的概念,若是看不懂以上例子,跳過便是。 下面再來看其他的使用場景。 Deducing this還可以用來解決根據(jù)closure類型完美轉(zhuǎn)發(fā)Lambda捕獲參數(shù)的問題。 亦即,如果Lambda函數(shù)的類型為左值,那么捕獲的參數(shù)就以左值轉(zhuǎn)發(fā);如果為右值,那么就以右值轉(zhuǎn)發(fā)。下面是一個(gè)例子:
 1#include
 2#include
 3#include//forstd::forward_like
 4
 5autoget_message(){
 6return42;
 7}
 8
 9structScheduler{
10autosubmit(auto&&m){
11std::cout<::value<::value<bool{
21returnscheduler.submit(std::forward_like(m));
22};
23callback();//retry(callback)
24std::move(callback)();//try-or-fail(rvalue)
25}
26
27//Output:
28//true
29//false
30//false
31//true
若是沒有Deducing this,那么將無法簡單地完成這個(gè)操作。 另一個(gè)用處是可以將this以值形式傳遞,對于小對象來說,可以提高性能。 一個(gè)例子:
 1structS{
 2intdata_;
 3intfoo();//implicitthispointer
 4//intfoo(thisS);//Passthisbyvalue
 5};
 6
 7intmain(){
 8Ss{42};
 9returns.foo();
10}
11
12// implicit this pointer生成的匯編代碼:
13//subrsp,40;00000028H
14//learcx,QWORDPTRs$[rsp]
15//movDWORDPTRs$[rsp],42;0000002aH
16//callintS::foo(void);S::foo
17//addrsp,40;00000028H
18//ret0
19
20// Pass this by value生成的匯編代碼:
21//movecx,42;0000002aH
22//jmpstaticintS::foo(thisS);S::foo
對于隱式的this指針,生成的匯編代碼需要先分配棧空間,保存this指針到rcx寄存器中,再將42賦值到data_中,然后調(diào)用foo(),最后平棧。 而以值形式傳遞this,則無需那些操作,因?yàn)橹祩鬟f的this不會(huì)影響s變量,中間的步驟都可以被優(yōu)化掉,也不再需要分配和平棧操作,所以可以直接將42保存到寄存器當(dāng)中,再jmp到foo()處執(zhí)行。 Deducing this是個(gè)單獨(dú)就可寫篇四五星難度文章的特性,用處很多,值得深入探索的地方也很多,所以即便是概述這部分也寫得比較多。

2Monadicstd::optional(P0798R8)

P0798提議為std::optional增加三個(gè)新的成員:map(), and_then()和or_else()。 功能分別為:

map:對optional的值應(yīng)用一個(gè)函數(shù),返回optional中wrapped的結(jié)果。若是optional中沒有值,返回一個(gè)空的optional;

and_then:組合使用返回optional的函數(shù);

or_else:若是有值,返回optional;若是無值,則調(diào)用傳入的函數(shù),在此可以處理錯(cuò)誤。

在R2中map()被重命名為transform(),因此實(shí)際新增的三個(gè)函數(shù)為transform(),and_then()和or_else()。 這些函數(shù)主要是避免手動(dòng)檢查optional值是否有效,比如:

1//Before
2if(opt_string){
3std::optionali=stoi(*opt_string);
4}
5
6//After
7std::optionali=opt_string.and_then(stoi);
一個(gè)使用的小例子:
1//chainaseriesoffunctionsuntilthere'sanerror
2std::optionalopt_string("10");
3std::optionali=opt_string
4.and_then(std::stoi)
5.transform([](autoi){returni*2;});
錯(cuò)誤的情況:
1//fails,transformnotcalled,j==nullopt
2std::optionalopt_string_bad("abcd");
3std::optionalj=opt_string_bad
4.and_then(std::stoi)
5.transform([](autoi){returni*2;});
目前GCC 12,Clang 14,MSVC v19.32已經(jīng)支持該特性。

3std::expected(P0323)

該特性用于解決錯(cuò)誤處理的問題,增加了一個(gè)新的頭文件。 錯(cuò)誤處理的邏輯關(guān)系為條件關(guān)系,若正確,則執(zhí)行A邏輯;若失敗,則執(zhí)行B邏輯,并需要知道確切的錯(cuò)誤信息,才能對癥下藥。 當(dāng)前的常用方式是通過錯(cuò)誤碼或異常,但使用起來還是多有不便。 std::expected表示期望,算是std::variant和std::optional的結(jié)合,它要么保留T(期望的類型),要么保留E(錯(cuò)誤的類型),它的接口又和std::optional相似。 一個(gè)簡單的例子:

 1enumclassStatus:uint8_t{
 2Ok,
 3connection_error,
 4no_authority,
 5format_error,
 6};
 7
 8boolconnected(){
 9returntrue;
10}
11
12boolhas_authority(){
13returnfalse;
14}
15
16boolformat(){
17returnfalse;
18}
19
20std::expectedread_data(){
21if(!connected())
22returnstd::unexpected{Status::connection_error};
23if(!has_authority())
24returnstd::unexpected{Status::no_authority};
25if(!format())
26returnstd::unexpected{Status::format_error};
27
28return{"myexpectedtype"};
29}
30
31
32intmain(){
33autoresult=read_data();
34if(result){
35std::cout<

這種方式無疑會(huì)簡化錯(cuò)誤處理的操作。

該特性目前在GCC 12,Clang 16(還未發(fā)布),MSVC v19.33已經(jīng)實(shí)現(xiàn)。

4MultidimensionalArrays(P2128)

這個(gè)特性用于訪問多維數(shù)組,之前C++operator[]只支持訪問單個(gè)下標(biāo),無法訪問多維數(shù)組。

因此要訪問多維數(shù)組,以前的方式是:

重載operator(),于是能夠以m(1, 2)來訪問第1行第2個(gè)元素。但這種方式容易和函數(shù)調(diào)用產(chǎn)生混淆;

重載operator[],并以std::initializer_list作為參數(shù),然后便能以m[{1, 2}]來訪問元素。但這種方式看著別扭。

鏈?zhǔn)芥溄觨perator[],然后就能夠以m[1][2]來訪問元素。同樣,看著別扭至極。

定義一個(gè)at()成員,然后通過at(1, 2)訪問元素。同樣不方便。

感謝該提案,在C++23,我們終于可以通過m[1, 2]這種方式來訪問多維數(shù)組。

一個(gè)例子:

 1template
 2structmatrix{
 3T&operator[](constsize_tr,constsize_tc)noexcept{
 4returndata_[r*C+c];
 5}
 6
 7constT&operator[](constsize_tr,constsize_tc)constnoexcept{
 8returndata_[r*C+c];
 9}
10
11private:
12std::arraydata_;
13};
14
15
16intmain(){
17matrixm;
18m[0,0]=0;
19m[0,1]=1;
20m[1,0]=2;
21m[1,1]=3;
22
23for(autoi=0;i

該特性目前在GCC 12和Clang 15以上版本已經(jīng)支持。

5if consteval(P1938)

該特性是關(guān)于immediate function的,即consteval function。 解決的問題其實(shí)很簡單,在C++20,consteval function可以調(diào)用constexpr function,而反過來卻不行。

 1constevalautobar(intm){
 2returnm*6;
 3}
 4
 5constexprautofoo(intm){
 6returnbar(m);
 7}
 8
 9
10intmain(){
11[[maybe_unused]]autores=foo(42);
12}
以上代碼無法編譯通過,因?yàn)閏onstexpr functiong不是強(qiáng)保證執(zhí)行于編譯期,在其中自然無法調(diào)用consteval function。 但是,即便加上if std::is_constant_evaluated()也無法編譯成功。
1constexprautofoo(intm){
2if(std::is_constant_evaluated()){
3returnbar(m);
4}
5return42;
6}
這就存在問題了,P1938通過if consteval修復(fù)了這個(gè)問題。在C++23,可以這樣寫:
1constexprautofoo(intm){
2ifconsteval{
3returnbar(m);
4}
5return42;
6}
該特性目前在GCC 12和Clang 14以上版本已經(jīng)實(shí)現(xiàn)。

6FormattedOutput(P2093)

該提案就是std::print(),之前已經(jīng)說過,這里再簡單地說下。

標(biāo)準(zhǔn)cout的設(shè)計(jì)非常糟糕,具體表現(xiàn)在:

可用性差,基本沒有格式化能力;

會(huì)多次調(diào)用格式化I/0函數(shù);

默認(rèn)會(huì)同步標(biāo)準(zhǔn)C,性能低;

內(nèi)容由參數(shù)交替組成,在多線程環(huán)境,內(nèi)容會(huì)錯(cuò)亂顯示;

二進(jìn)制占用空間大;

……

隨著Formatting Library加入C++20,已在fmt庫中使用多年的fmt::print()加入標(biāo)準(zhǔn)也是順理成章。

格式化輸出的目標(biāo)是要滿足:可用性、Unicode編碼支持、良好的性能,與較小的二進(jìn)制占用空間。為了不影響現(xiàn)有代碼,該特性專門加了一個(gè)新的頭文件,包含兩個(gè)主要函數(shù):

1#include
2
3intmain(){
4constchar*world="world";
5std::print("Hello{}",world);//doesn'tprintanewline
6std::println("Hello{}",world);//printanewline
7}
這對cout來說絕對是暴擊,std::print的易用性和性能簡直完爆它。 其語法就是Formatting Library的格式化語法。 性能對比:
----------------------------------------------------------
BenchmarkTimeCPUIterations
----------------------------------------------------------
printf87.0ns86.9ns7834009
ostream255ns255ns2746434
print78.4ns78.3ns9095989
print_cout89.4ns89.4ns7702973
print_cout_sync91.5ns91.4ns7903889
結(jié)果顯示,printf與print幾乎要比cout快三倍,print默認(rèn)會(huì)打印到stdout。當(dāng)打印到cout并同步標(biāo)準(zhǔn)C的流時(shí)(print_cout_sync),print大概要快14%;當(dāng)不同步標(biāo)準(zhǔn)C的流時(shí)(print_cout),依舊要快不少。 遺憾的是,該特性目前沒有編譯器支持。

7FormattingRanges(P2286)

同樣屬于Formatting大家族,該提案使得我們能夠格式化輸出Ranges。 也就是說,我們能夠?qū)懗鲞@樣的代碼:

importstd;

automain()->int{
std::vectorvec{1,2,3};
std::print("{}
",vec);//Output:[1,2,3]
}
這意味著再也不用迭代來輸出Ranges了。 這是非常有必要的,考慮一個(gè)簡單的需求:文本分割。 Python的實(shí)現(xiàn):
1print("howyoudoing".split(""))
2
3#Output:
4#['how','you','doing']
Java的實(shí)現(xiàn):
 1importjava.util.Arrays;
 2
 3classMain{
 4publicstaticvoidmain(Stringargs[]){
 5System.out.println("howyoudoing".split(""));
 6System.out.println(Arrays.toString("howyoudoing".split("")));
 7}
 8}
 9
10//Output:
11//[Ljava.lang.String;@2b2fa4f7
12//[how,you,doing]
Rust的實(shí)現(xiàn):
 1useitertools::Itertools;
 2
 3fnmain(){
 4println!("{:?}","Howyoudoing".split(''));
 5println!("[{}]","Howyoudoing".split('').format(","));
 6println!("{:?}","Howyoudoing".split('').collect::>());
 7}
 8
 9//Output:
10//Split(SplitInternal{start:0,end:13,matcher:CharSearcher{haystack:"Howyoudoing",finger:0,finger_back:13,needle:'',utf8_size:1,utf8_encoded:[32,0,0,0]},allow_trailing_empty:true,finished:false})
11//[How,you,doing]
12//["How","you","doing"]
JS的實(shí)現(xiàn):
1console.log('Howyoudoing'.split(''))
2
3//Output:
4//["How","you","doing"]
Go的實(shí)現(xiàn):
 1packagemain
 2import"fmt"
 3import"strings"
 4
 5funcmain(){
 6fmt.Println(strings.Split("Howyoudoing",""));
 7}
 8
 9//Output:
10//[Howyoudoing]
Kotlin的實(shí)現(xiàn):
1funmain(){
2println("Howyoudoing".split(""));
3}
4
5//Output:
6//[How,you,doing]
C++的實(shí)現(xiàn):
 1intmain(){
 2std::string_viewcontents{"Howyoudoing"};
 3
 4autowords=contents
 5|std::split('')
 6|std::transform([](auto&&str){
 7returnstd::string_view(&*str.begin(),std::distance(str));
 8});
 9
10std::cout<
借助fmt,可以簡化代碼:
 1intmain(){
 2std::string_viewcontents{"Howyoudoing"};
 3
 4autowords=contents
 5|std::split('')
 6|std::transform([](auto&&str){
 7returnstd::string_view(&*str.begin(),std::distance(str));
 8});
 9
10fmt::print("{}
",words); 
11fmt::print("<<{}>>",fmt::join(words,"--"));
12
13}
14
15//Output:
16//["How","you","doing"]
17//<>

因?yàn)関iews::split()返回的是一個(gè)subrange,因此需要將其轉(zhuǎn)變成string_view,否則,輸出將為:

 1intmain(){
 2std::string_viewcontents{"Howyoudoing"};
 3
 4autowords=contents|std::split('');
 5
 6fmt::print("{}
",words);
 7fmt::print("<<{}>>",fmt::join(words,"--"));
 8
 9}
10
11//Output:
12//[[H,o,w],[y,o,u],[d,o,i,n,g]]
13//<<['H',?'o',?'w']--['y',?'o',?'u']--['d',?'o',?'i',?'n',?'g']>>

總之,這個(gè)特性將極大簡化Ranges的輸出,是值得興奮的特性之一。

該特性目前沒有編譯器支持。

7import std(P2465)

C++20模塊很難用的一個(gè)原因就是標(biāo)準(zhǔn)模塊沒有提供,因此這個(gè)特性的加入是自然趨勢。

現(xiàn)在,可以寫出這樣的代碼:

1importstd;
2
3intmain(){
4std::print("Hellostandardlibrarymodules!
");
5}

性能對比:

6a02ac70-a29e-11ed-bfe3-dac502259ad0.png

如何你是混合C和C++,那可以使用std.compat module,所有的C函數(shù)和標(biāo)準(zhǔn)庫函數(shù)都會(huì)包含進(jìn)來。

目前基本沒有編譯器支持此特性。

8out_ptr(P1132r8)

23新增了兩個(gè)對于指針的抽象類型,std::out_ptr_t和std::inout_ptr_t,兩個(gè)新的函數(shù)std::out_ptr()和std::inout_ptr()分別返回這兩個(gè)類型。 主要是在和C API交互時(shí)使用的,一個(gè)例子對比一下:

 1//Before
 2intold_c_api(int**);
 3
 4intmain(){
 5autoup=std::make_unique(5);
 6
 7int*up_raw=up.release();
 8if(intec=foreign_resetter(&up)){
 9returnec;
10}
11
12up.reset(up_raw);
13}
14
15////////////////////////////////
16//After
17intold_c_api(int**);
18
19intmain(){
20autoup=std::make_unique(5);
21
22if(intec=foreign_resetter(std::inout_ptr(up))){
23returnec;
24}
25
26//*upisstillvalid
27}

該特性目前在MSVC v19.30支持。

9auto(x) decay copy(P0849)

該提案為auto又增加了兩個(gè)新語法:auto(x)和auto{x}。兩個(gè)作用一樣,只是寫法不同,都是為x創(chuàng)建一份拷貝。

為什么需要這么個(gè)東西?

看一個(gè)例子:

1voidbar(constauto&);
2
3voidfoo(constauto¶m){
4autocopy=param;
5bar(copy);
6}
foo()中調(diào)用bar(),希望傳遞一份param的拷貝,則我們需要單獨(dú)多聲明一個(gè)臨時(shí)變量。或是這樣:
1voidfoo(constauto¶m){
2bar(std::decay_t{param});
3}
這種方式需要手動(dòng)去除多余的修飾,只留下T,要更加麻煩。 auto(x)就是內(nèi)建的decay copy,現(xiàn)在可以直接這樣寫:
1voidfoo(constauto¶m){
2bar(auto{param});
3}
大家可能還沒意識(shí)到其必要性,來看提案當(dāng)中更加復(fù)雜一點(diǎn)的例子。
 1voidpop_front_alike(auto&container){
 2std::erase(container,container.front());
 3}
 4
 5intmain(){
 6std::vectorfruits{"apple","apple","cherry","grape",
 7"apple","papaya","plum","papaya","cherry","apple"};
 8pop_front_alike(fruits);
 9
10fmt::print("{}
",fruits);
11}
12
13//Output:
14//["cherry","grape","apple","papaya","plum","papaya","apple"]

請注意該程序的輸出,是否如你所想的一樣。若沒有發(fā)現(xiàn)問題,請讓我再提醒一下:pop_front_alike()要移除容器中所有跟第1個(gè)元素相同的元素。

因此,理想的結(jié)果應(yīng)該為:

["cherry","grape","papaya","plum","papaya","cherry"]
是哪里出了問題呢?讓我們來看看gcc std::erase()的實(shí)現(xiàn):
 1template
 2_ForwardIterator
 3__remove_if(_ForwardIterator__first,_ForwardIterator__last,
 4_Predicate__pred)
 5{
 6__first=std::__find_if(__first,__last,__pred);
 7if(__first==__last)
 8return__first;
 9_ForwardIterator__result=__first;
10++__first;
11for(;__first!=__last;++__first)
12if(!__pred(__first)){
13*__result=_GLIBCXX_MOVE(*__first);
14++__result;
15}
16
17return__result;
18}
19
20template
21inlinetypenamevector<_Tp,?_Alloc>::size_type
22erase(vector<_Tp,?_Alloc>&__cont,const_Up&__value)
23{
24constauto__osz=__cont.size();
25__cont.erase(std::remove(__cont.begin(),__cont.end(),__value),
26__cont.end());
27return__osz-__cont.size();
28}

std::remove()最終調(diào)用的是remove_if(),因此關(guān)鍵就在這個(gè)算法里面。這個(gè)算法每次會(huì)比較當(dāng)前元素和欲移除元素,若不相等,則用當(dāng)前元素覆蓋當(dāng)前__result迭代器的值,然后__result向后移一位。重復(fù)這個(gè)操作,最后全部有效元素就都跑到__result迭代器的前面去了。

問題出在哪里呢?欲移除元素始終指向首個(gè)元素,而它會(huì)隨著元素覆蓋操作被改變,因?yàn)樗念愋蜑閏onst T&。

此時(shí),必須重新copy一份值,才能得到正確的結(jié)果。

故將代碼小作更改,就能得到正確的結(jié)果。

voidpop_front_alike(auto&container){
autocopy=container.front();
std::erase(container,copy);
}

然而這種方式是非常反直覺的,一般來說這兩種寫法的效果應(yīng)該是等價(jià)的。

我們將copy定義為一個(gè)單獨(dú)的函數(shù),表達(dá)效果則要好一點(diǎn)。

autocopy(constauto&value){
returnvalue;
}

voidpop_front_alike(auto&container){
std::erase(container,copy(container.front()));
}

而auto{x}和auto(x),就相當(dāng)于這個(gè)copy()函數(shù),只不過它是內(nèi)建到語言里面的而已。

10Narrowingcontextualconversions to bool

這個(gè)提案允許在static_assert和if constexpr中從整形轉(zhuǎn)換為布爾類型。

以下表格就可以表示所有內(nèi)容。

Before After
if constexpr(bool(flags & Flags::Exec)) if constexpr(flags & Flags::Exec)
if constexpr(flags & Flags::Exec != 0) if constexpr(flags & Flags::Exec)
static_assert(N % 4 != 0); static_assert(N % 4);
static_assert(bool(N)); static_assert(N);

對于嚴(yán)格的C++編譯器來說,以前在這種情境下int無法向下轉(zhuǎn)換為bool,需要手動(dòng)強(qiáng)制轉(zhuǎn)換,C++23這一情況得到了改善。

目前在GCC 9和Clang 13以上版本支持該特性。

11forward_like(P2445)

這個(gè)在Deducing this那節(jié)已經(jīng)使用過了,是同一個(gè)作者。 使用情境讓我們回顧一下這個(gè)例子:

1autocallback=[m=get_message(),&scheduler](thisauto&&self)->bool{
2returnscheduler.submit(std::forward_like(m));
3};
4
5callback();//retry(callback)
6std::move(callback)();//try-or-fail(rvalue)

std::forward_like加入到了中,就是根據(jù)模板參數(shù)的值類別來轉(zhuǎn)發(fā)參數(shù)。

如果closure type為左值,那么m將轉(zhuǎn)發(fā)為左值;如果為右值,將轉(zhuǎn)發(fā)為右值。

聽說Clang 16和MSVC v19.34支持該特性,但都尚未發(fā)布。

12#eifdef and #eifndef(P2334)

這兩個(gè)預(yù)處理指令來自WG14(C的工作組),加入到了C23。C++為了兼容C,也將它們加入到了C++23。

也是一個(gè)完善工作。

#ifdef和#ifndef分別是#if defined()和#if !defined()的簡寫,而#elif defined()和#elif !defined()卻并沒有與之對應(yīng)的簡寫指令。因此,C23使用#eifdef和#eifndef來補(bǔ)充這一遺漏。

總之,是兩個(gè)非常簡單的小特性。目前已在GCC 12和Clang 13得到支持。

13#warning(P2437)

#warning是主流編譯器都會(huì)支持的一個(gè)特性,最終倒逼C23和C++23也加入了進(jìn)來。 這個(gè)小特性可以用來產(chǎn)生警告信息,與#error不同,它并不會(huì)停止翻譯。 用法很簡單:

1#ifndefFOO
2#warning"FOOdefined,performancemightbelimited"
3#endif

目前MSVC不支持該特性,其他主流編譯器都支持。

14constexpr std::unique_ptr(P2273R3)

std::unique_ptr也支持編譯期計(jì)算了,一個(gè)小例子:

1constexprautofun(){
2autop=std::make_unique(4);
3return*p;
4}
5
6intmain(){
7constexprautoi=fun();
8static_assert(4==i);
9}

目前GCC 12和MSVC v19.33支持該特性。

15improving string and string_view(P1679R3, P2166R1, P1989R2, P1072R10, P2251R1)

string和string_view也獲得了一些增強(qiáng),這里簡單地說下。

P1679為二者增加了一個(gè)contain()函數(shù),小例子:

1std::stringstr("dummytext");
2if(str.contains("dummy")){
3//dosomething
4}
目前GCC 11,Clang 12,MSVC v19.30支持該特性。 P2166使得它們從nullptr構(gòu)建不再產(chǎn)生UB,而是直接編譯失敗。
1std::strings{nullptr};//error!
2std::string_viewsv{nullptr};//error!
目前GCC 12,Clang 13,MSVC v19.30支持該特性。 P1989是針對std::string_view的,一個(gè)小例子搞定:
1intmain(){
2std::vectorv{'a','b','c'};
3
4//Before
5std::string_viewsv(v.begin(),v.end());
6
7//After
8std::string_viewsv23{v};
9}

以前無法直接從Ranges構(gòu)建std::string_view,而現(xiàn)在支持這種方式。

該特性在GCC 11,Clang 14,MSVC v19.30已經(jīng)支持。

P1072為string新增了一個(gè)成員函數(shù):

1template
2constexprvoidresize_and_overwrite(size_typecount,Operationop);
可以通過提案中的一個(gè)示例來理解:
1intmain(){
2std::strings{"Food:"};
3
4s.resize_and_overwrite(10,[](char*buf,intn){
5returnstd::find(buf,buf+n,':')-buf;
6});
7
8std::cout<

主要是兩個(gè)操作:改變大小和覆蓋內(nèi)容。第1個(gè)參數(shù)是新的大小,第2個(gè)參數(shù)是一個(gè)op,用于設(shè)置新的內(nèi)容。

然后的邏輯是:

如果maxsize <= s.size(),刪除最后的size()-maxsize個(gè)元素;

如果maxsize > s.size(),追加maxsize-size()個(gè)默認(rèn)元素;

調(diào)用erase(begin() + op(data(), maxsize), end())。

這里再給出一個(gè)例子,可以使用上面的邏輯來走一遍,以更清晰地理解該函數(shù)。

 1constexprstd::string_viewfruits[]{"apple","banana","coconut","date","elderberry"};
 2std::strings1{"Food:"};
 3
 4s1.resize_and_overwrite(16,[sz=s1.size()](char*buf,std::size_tbuf_size){
 5constautoto_copy=std::min(buf_size-sz,fruits[1].size());//6
 6std::memcpy(buf+sz,fruits[1].data(),to_copy);//append"banana"tos1.
 7returnsz+to_copy;//6+6
 8});
 9
10std::cout<

注意一下,maxsize是最大的可能大小,而op返回才是實(shí)際大小,因此邏輯的最后才有一個(gè)erase()操作,用于刪除多余的大小。

這個(gè)特性在GCC 12,Clang 14,MSVC v19.31已經(jīng)實(shí)現(xiàn)。

接著來看P2251,它更新了std::span和std::string_view的約束,從C++23開始,它們必須滿足TriviallyCopyable Concept。

主流編譯器都支持該特性。

最后來看P0448,其引入了一個(gè)新的頭文件

大家都知道,stringstream現(xiàn)在被廣泛使用,可以將數(shù)據(jù)存儲(chǔ)到string或vector當(dāng)中,但這些容器當(dāng)數(shù)據(jù)增長時(shí)會(huì)發(fā)生「挪窩」的行為,若是不想產(chǎn)生這個(gè)開銷呢?

提供了一種選擇,你可以指定固定大小的buffer,它不會(huì)重新分配內(nèi)存,但要小心數(shù)據(jù)超出buffer大小,此時(shí)內(nèi)存的所有權(quán)在程序員這邊。

一個(gè)小例子:

 1#defineASSERT_EQUAL(a,b)assert(a==b)
 2#defineASSERT(a)assert(a)
 3
 4intmain(){
 5charinput[]="102030";
 6std::ispanstreamis{std::span{input}};
 7inti;
 8
 9is>>i;
10ASSERT_EQUAL(10,i);
11
12is>>i;
13ASSERT_EQUAL(20,i);
14
15is>>i;
16ASSERT_EQUAL(30,i);
17
18is>>i;
19ASSERT(!is);
20}

目前GCC 12和MSVC v19.31已支持該特性。

16staticoperator()(P1169R4)

因?yàn)楹瘮?shù)對象,Lambdas使用得越來越多,經(jīng)常作為標(biāo)準(zhǔn)庫的定制點(diǎn)使用。這種函數(shù)對象只有一個(gè)operator (),如果允許聲明為static,則可以提高性能。

至于原理,大家可以回顧一下Deducing this那節(jié)的Pass this by value提高性能的原理。明白靜態(tài)函數(shù)和非靜態(tài)函數(shù)在重載決議中的區(qū)別,大概就能明白這點(diǎn)。

順便一提,由于mutidimensional operator[]如今已經(jīng)可以達(dá)到和operator()一樣的效果,它也可以作為一種新的函數(shù)語法,你完全可以這樣調(diào)用foo[],只是不太直觀。因此,P2589也提議了static operator[]

17std::unreachable(P0627R6)

當(dāng)我們知道某個(gè)位置是不可能執(zhí)行到,而編譯器不知道時(shí),使用std::unreachalbe可以告訴編譯器,從而避免沒必要的運(yùn)行期檢查。 一個(gè)簡單的例子:

 1voidfoo(inta){
 2switch(a){
 3case1:
 4//dosomething
 5break;
 6case2:
 7//dosomething
 8break;
 9default:
10std::unreachable();
11}
12}
13
14boolis_valid(inta){
15returna==1||a==2;
16}
17
18intmain(){
19inta=0;
20while(!is_valid(a))
21std::cin>>a;
22foo(a);
23}
該特性位于,在GCC 12,Clang 15和MSVC v19.32已經(jīng)支持。

18std::to_underlying(P1682R3)

同樣位于,用于枚舉到其潛在的類型,相當(dāng)于以下代碼的語法糖:

static_cast>(e);

一個(gè)簡單的例子就能看懂:

 1voidprint_day(inta){
 2fmt::print("{}
",a);
 3}
 4
 5enumclassDay:std::uint8_t{
 6Monday=1,
 7Tuesday,
 8Wednesday,
 9Thursday,
10Friday,
11Saturday,
12Sunday
13};
14
15
16intmain(){
17//Before
18print_day(static_cast>(Day::Monday));
19
20//C++23
21print_day(std::Friday));
22}
的確很簡單吧!

該特性目前在GCC 11,Clang 13,MSVC v19.30已經(jīng)實(shí)現(xiàn)。

19std::byteswap(P1272R4)

位于,顧名思義,是關(guān)于位操作的。

同樣,一個(gè)例子看懂:

 1template
 2voidprint_hex(Tv)
 3{
 4for(std::size_ti=0;i>=8)
 5{
 6fmt::print("{:02X}",static_cast(T(0xFF)&v));
 7}
 8std::cout<
可以看到,其作用是逆轉(zhuǎn)整型的字節(jié)序。當(dāng)需要在兩個(gè)不同的系統(tǒng)傳輸數(shù)據(jù),它們使用不同的字節(jié)序時(shí)(大端小端),這個(gè)工具就會(huì)很有用。

該特性目前在GCC 12,Clang 14和MSVC v19.31已經(jīng)支持。

20std::stacktrace(P0881R7, P2301R1)

位于,可以讓我們捕獲調(diào)用棧的信息,從而知道哪個(gè)函數(shù)調(diào)用了當(dāng)前函數(shù),哪個(gè)調(diào)用引發(fā)了異常,以更好地定位錯(cuò)誤。

一個(gè)小例子:

1voidfoo(){
2autotrace=std::current();
3std::cout<

輸出如下。

0#foo()at/app/example.cpp:5
1#at/app/example.cpp:10
2#at:0
3#at:0
4#

注意,目前GCC 12.1和MSVC v19.34支持該特性,GCC編譯時(shí)要加上-lstdc++_libbacktrace參數(shù)。

std::stacktrace是std::basic_stacktrace使用默認(rèn)分配器時(shí)的別名,定義為:

usingstacktrace=std::basic_stacktrace>;

而P2301,則是為其添加了PMR版本的別名,定義為:

namespacepmr{
usingstacktrace=
std::basic_stacktrace>;
}

于是使用起來就會(huì)方便一些。

 1//Before
 2charbuffer[1024];
 3
 4std::monotonic_buffer_resourcepool{
 5std::data(buffer),std::size(buffer)};
 6
 7std::basic_stacktrace<
 8????std::polymorphic_allocator>
 9trace{&pool};
10
11//After
12charbuffer[1024];
13
14std::monotonic_buffer_resourcepool{
15std::data(buffer),std::size(buffer)};
16
17std::stacktracetrace{&pool};

這個(gè)特性到時(shí)再單獨(dú)寫篇文章,在此不細(xì)論。

21Attributes(P1774R8, P2173R1, P2156R1)

Attributes在C++23也有一些改變。

首先,P1774新增了一個(gè)Attribute [[assume]],其實(shí)在很多編譯器早已存在相應(yīng)的特性,例如__assume()(MSVC, ICC),__builtin_assume()(Clang)。GCC沒有相關(guān)特性,所以它也是最早實(shí)現(xiàn)標(biāo)準(zhǔn)[[assume]]的,目前就GCC 13支持該特性(等四月發(fā)布,該版本對Rangs的支持也很完善)。

現(xiàn)在可以通過宏來玩:

1#ifdefined(__clang__)
2#defineASSUME(expr)__builtin_assume(expr)
3#elifdefined(__GNUC__)&&!defined(__ICC)
4#defineASSUME(expr)if(expr){}else{__builtin_unreachable();}
5#elifdefined(_MSC_VER)||defined(__ICC)
6#defineASSUME(expr)__assume(expr)
7#endif
論文當(dāng)中的一個(gè)例子:
1voidlimiter(float*data,size_tsize){
2ASSUME(size>0);
3ASSUME(size%32==0);
4
5for(size_ti=0;i

第一個(gè)是假設(shè)size永不為0,總是正數(shù);第二個(gè)告訴編譯器size總是32的倍數(shù);第三個(gè)表明數(shù)據(jù)不是NaN或無限小數(shù)。

這些假設(shè)不會(huì)被評估,也不會(huì)被檢查,編譯器假設(shè)其為真,依此優(yōu)化代碼。若是假設(shè)為假,可能會(huì)產(chǎn)生UB。

使用該特性與否編譯產(chǎn)生的指令數(shù)對比結(jié)果如下圖。 6a146b72-a29e-11ed-bfe3-dac502259ad0.png

其次,P2173使得可以在Lambda表達(dá)式上使用Attributes,一個(gè)例子:

 1//Anyattributesospecifieddoesnotappertaintothefunction
 2//calloperatororoperatortemplateitself,butitstype.
 3autolam=[][[nodiscard]]->int{return42;};
 4
 5intmain()
 6{
 7lam();
 8}
 9
10//Output:
11//:Infunction'intmain()':
12//8:warning:ignoringreturnvalueof'',declaredwithattribute'nodiscard'[-Wunused-result]
13//12|lam();
14//|~~~^~
15//12:note:declaredhere
16//8|autolam=[][[nodiscard]]->int{return42;};
17//|^

注意,Attributes屬于closure type,而不屬于operator ()。

因此,有些Attributes不能使用,比如[[noreturn]],它表明函數(shù)的控制流不會(huì)返回到調(diào)用方,而對于Lambda函數(shù)是會(huì)返回的。

除此之外,此處我還展示了C++的另一個(gè)Lambda特性。

在C++23之前,最簡單的Lambda表達(dá)式為[](){},而到了C++23,則是[]{},可以省略無參時(shí)的括號(hào),這得感謝P1102。

早在GCC 9就支持Attributes Lambda,Clang 13如今也支持。

最后來看P2156,它移除了重復(fù)Attributes的限制。

簡單來說,兩種重復(fù)Attributes的語法評判不一致。例子:

1//Notallow
2[[nodiscard,nodiscard]]autofoo(){
3return42;
4}
5
6//Allowed
7[[nodiscard]][[nodiscard]]autofoo(){
8return42;
9}

為了保證一致性,去除此限制,使得標(biāo)準(zhǔn)更簡單。

什么時(shí)候會(huì)出現(xiàn)重復(fù)Attributes,看論文怎么說:

During this discussion, it was brought up that
the duplication across attribute-specifiers are to support cases where macros are used to conditionally add attributes to an
attribute-specifier-seq, however it is rare for macros to be used to generate attributes within the same attribute-list. Thus,
removing the limitation for that reason is unnecessary.

在基于宏生成的時(shí)候可能會(huì)出現(xiàn)重復(fù)Attributes,因此允許第二種方式;宏生成很少使用第一種形式,因此標(biāo)準(zhǔn)限制了這種情況。但這卻并沒有讓標(biāo)準(zhǔn)變得更簡單。因此,最終移除了該限制。

目前使用GCC 11,Clang 13以上兩種形式的結(jié)果將保持一致。

22Lambdas(P1102R2, P2036R3, P2173R1)

Lambdas表達(dá)式在C++23也再次迎來了一些新特性。

像是支持Attributes,可以省略(),這在Attributes這一節(jié)已經(jīng)介紹過,不再贅述。

另一個(gè)新特性是P2036提的,接下來主要說說這個(gè)。

這個(gè)特性改變了trailing return typesName Lookup規(guī)則,為什么?讓我們來看一個(gè)例子。

1doublej=42.0;
2//...
3autocounter=[j = 0]()mutable->decltype(j){
4returnj++;
5};

counter最終的類型是什么?是int嗎?還是double?其實(shí)是double。

無論捕獲列表當(dāng)中存在什么值,trailing return type的Name Lookup都不會(huì)查找到它。

這意味著單獨(dú)這樣寫將會(huì)編譯出錯(cuò):

1autocounter=[j=0]()mutable->decltype(j){
2returnj++;
3};
4
5//Output:
6//44:error:useofundeclaredidentifier'j'
7//autocounter=[j=0]()mutable->decltype(j){
8//^

因?yàn)閷τ趖railing return type來說,根本就看不見捕獲列表中的j。

以下例子能夠更清晰地展示這個(gè)錯(cuò)誤:

1templateintbar(int&,T&&);//#1
2templatevoidbar(intconst&,T&&);//#2
3
4inti;
5autof=[=](auto&&x)->decltype(bar(i,x)){
6returnbar(i,x);
7}
8
9f(42);//error
在C++23,trailing return types的Name Lookup規(guī)則變?yōu)椋涸谕獠坎檎抑埃炔檎也东@列表,從而解決這個(gè)問題。 目前沒有任何編譯器支持該特性。

23Literal suffixes for (signed) size_t(P0330R8)

這個(gè)特性為std::size_t增加了后綴uz,為signed std::size_t加了后綴z。

有什么用呢?看個(gè)例子:

1#include
2
3intmain(){
4std::vectorv{0,1,2,3};
5for(autoi=0u,s=v.size();i

這代碼在32 bit平臺(tái)編譯能夠通過,而放到64 bit平臺(tái)編譯,則會(huì)出現(xiàn)錯(cuò)誤:

1(5):errorC3538:inadeclarator-list'auto'mustalwaysdeducetothesametype
2(5):note:couldbe'unsignedint'
3(5):note:or'unsigned__int64'

在32 bit平臺(tái)上,i被推導(dǎo)為unsigned int,v.size()返回的類型為size_t。而size_t在32 bit上為unsigned int,而在64 bit上為unsigned long long。(in MSVC)

因此,同樣的代碼,從32 bit切換到64 bit時(shí)就會(huì)出現(xiàn)錯(cuò)誤。

而通過新增的后綴,則可以保證這個(gè)代碼在任何平臺(tái)上都能有相同的結(jié)果。

1#include
2
3intmain(){
4std::vectorv{0,1,2,3};
5for(autoi=0uz,s=v.size();i

如此一來就解決了這個(gè)問題。

目前GCC 11和Clang 13支持該特性。

24std::mdspan(P0009r18)

std::mdspan是std::span的多維版本,因此它是一個(gè)多維Views。 看一個(gè)例子,簡單了解其用法。

 1intmain()
 2{
 3std::vectorv={1,2,3,4,5,6,7,8,9,10,11,12};
 4
 5//Viewdataascontiguousmemoryrepresenting2rowsof6intseach
 6automs2=std::mdspan(v.data(),2,6);
 7//Viewthesamedataasa3Darray2x3x2
 8automs3=std::mdspan(v.data(),2,3,2);
 9
10//writedatausing2Dview
11for(size_ti=0;i!=ms2.extent(0);i++)
12for(size_tj=0;j!=ms2.extent(1);j++)
13ms2[i,j]=i*1000+j;
14
15//readbackusing3Dview
16for(size_ti=0;i!=ms3.extent(0);i++)
17{
18fmt::print("slice@i={}
",i);
19for(size_tj=0;j!=ms3.extent(1);j++)
20{
21for(size_tk=0;k!=ms3.extent(2);k++)
22fmt::print("{}",ms3[i,j,k]);
23fmt::print("
");
24}
25}
26}
目前沒有編譯器支持該特性,使用的是https://raw.githubusercontent.com/kokkos/mdspan/single-header/mdspan.hpp實(shí)現(xiàn)的版本,所以在experimental下面。 ms2是將數(shù)據(jù)以二維形式訪問,ms3則以三維訪問,Views可以改變原有數(shù)據(jù),因此最終遍歷的結(jié)果為:
1slice@i=0
201
323
445
5slice@i=1
610001001
710021003
810041005
這個(gè)特性值得剖析下其設(shè)計(jì),這里不再深究,后面單獨(dú)出一篇文章。

25flat_map, flat_set(P0429R9, P1222R4)

C++23多了flat version的map和set:

flat_map

flat_set

flat_multimap

flat_multiset

過去的容器,有的使用二叉樹,有的使用哈希表,而flat版本的使用的連續(xù)序列的容器,更像是容器的適配器。

無非就是時(shí)間或空間復(fù)雜度的均衡,目前沒有具體測試,也沒有編譯器支持,暫不深究。

26總結(jié)

本篇已經(jīng)夠長了,C++23比較有用的特性基本都包含進(jìn)來了。

其中的另一個(gè)重要更新Ranges并沒有包含。讀至此,大家應(yīng)該已經(jīng)感覺到C++23在于完善,而不在于增加。沒有什么全新的東西,也沒什么太大的特性,那些就得等到C++26了。

大家喜歡哪些C++23特性?

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 參數(shù)
    +關(guān)注

    關(guān)注

    11

    文章

    1834

    瀏覽量

    32221
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62618
  • 指針
    +關(guān)注

    關(guān)注

    1

    文章

    480

    瀏覽量

    70563
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

    73651

原文標(biāo)題:C++23 特性概覽

文章出處:【微信號(hào):CPP開發(fā)者,微信公眾號(hào):CPP開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    LLVM clang 公開 -std=c++23

    C++2a。 C++23 標(biāo)準(zhǔn)引入了許多新的特性,例如基于 Boost stacktrace 的 stacktrace library、conditionally borrowed ranges、兼容
    發(fā)表于 05-27 11:29

    EIA 364 23C

    EIA STANDARD TP-23CLOW LEVEL CONTACT RESISTANCETEST PROCEDURE FOR ELECTRICALCONNECTORS AND SOCKETSEIA/ECA-364-23C(Revision of EIA-364-
    發(fā)表于 08-10 18:43 ?34次下載

    低功耗數(shù)字VLSI設(shè)計(jì):概覽

    低功耗數(shù)字VLSI設(shè)計(jì):概覽:
    發(fā)表于 07-25 16:44 ?0次下載
    低功耗數(shù)字VLSI設(shè)計(jì):<b class='flag-5'>概覽</b>

    MEDICI的語法概覽

    MEDICI 的語法概覽 語句簡介
    發(fā)表于 08-27 18:01 ?0次下載

    Apple平板iPad 概覽

    Apple平板iPad 概覽 規(guī)格: 尺寸:242.8
    發(fā)表于 02-01 11:26 ?1077次閱讀

    C8051F單片機(jī)產(chǎn)品概覽

    C8051F單片機(jī)產(chǎn)品概覽,又需要的朋友下來看看
    發(fā)表于 05-06 11:47 ?0次下載

    F2產(chǎn)品技術(shù)培訓(xùn)_1.產(chǎn)品特性概覽

    F2產(chǎn)品技術(shù)培訓(xùn)_1.產(chǎn)品特性概覽
    發(fā)表于 03-15 15:10 ?6次下載

    C#23種設(shè)計(jì)模式【完整】

    C#23種設(shè)計(jì)模式
    發(fā)表于 08-21 17:38 ?71次下載

    機(jī)器學(xué)習(xí)概覽

    機(jī)器學(xué)習(xí)概覽
    發(fā)表于 09-07 11:11 ?4次下載
    機(jī)器學(xué)習(xí)<b class='flag-5'>概覽</b>

    CAT-D38999-DTS23C CAT-D38999-DTS23C 標(biāo)準(zhǔn)圓形連接器

    電子發(fā)燒友網(wǎng)為你提供TE(ti)CAT-D38999-DTS23C相關(guān)產(chǎn)品參數(shù)、數(shù)據(jù)手冊,更有CAT-D38999-DTS23C的引腳圖、接線圖、封裝手冊、中文資料、英文資料,CAT-D38999-DTS23C真值表,CAT-D
    發(fā)表于 07-30 17:00

    Eagle器件概覽

    電子發(fā)燒友網(wǎng)站提供《Eagle器件概覽.pdf》資料免費(fèi)下載
    發(fā)表于 09-27 09:22 ?3次下載
    Eagle器件<b class='flag-5'>概覽</b>

    SALEAGLE FPGA器件概覽

    電子發(fā)燒友網(wǎng)站提供《SALEAGLE FPGA器件概覽 .pdf》資料免費(fèi)下載
    發(fā)表于 09-27 09:16 ?0次下載
    SALEAGLE FPGA器件<b class='flag-5'>概覽</b>

    C++23C++26新標(biāo)準(zhǔn)的展望

    而相對于C++23,個(gè)人更期待C++26,因?yàn)樗鼤?huì)引入很多重磅的東西:executors、network、static reflection,希望到26時(shí)真的能看到這些,到時(shí)候C++也確實(shí)就更完善了。
    的頭像 發(fā)表于 10-19 09:49 ?8360次閱讀

    模擬輸出及架構(gòu)概覽

    模擬輸出及架構(gòu)概覽
    發(fā)表于 11-04 09:52 ?3次下載
    模擬輸出及架構(gòu)<b class='flag-5'>概覽</b>

    LCD驅(qū)動(dòng)控制AiP16C23,可替代合泰HT16C23

    中微愛芯國產(chǎn)LCD驅(qū)動(dòng)控制芯片AiP16C23,可替代合泰HT16C23??價(jià)格便宜,亮點(diǎn)十足 AiP16C23是一款標(biāo)準(zhǔn)I2C接口通訊LCD控制/驅(qū)動(dòng)芯片。該芯片提供1/4占空比和
    發(fā)表于 12-30 14:17 ?1028次閱讀
    LCD驅(qū)動(dòng)控制AiP16<b class='flag-5'>C23</b>,可替代合泰HT16<b class='flag-5'>C23</b>
    主站蜘蛛池模板: 亚洲综合在线最大成人| 天天射夜夜爽| 国产91色综合久久免费分享| 国模私拍一区二区三区| 成人网在线观看| 在线亚洲精品中文字幕美乳| 怡红院影院| 欧美午夜精品| bt天堂网在线| 亚洲一区色| 中文字幕不卡在线播放| 婷婷激情电影| 日本特黄特色大片免费播放视频| 欧美三级色| 国产精品久久久久久久久| 在线亚洲精品中文字幕美乳| 日日操夜夜操天天操| 91成人免费福利网站在线| 一区二区福利| 伊人久久综合网站| 日韩久久精品视频| 激情综合视频| 亚洲精品影视| 国产女同在线观看| 32pao强力打造免费高速高清| 好紧好湿好黄的视频| 无限国产资源| 免费艹逼视频| 伊人网在线视频观看| 加勒比在线一区| 亚洲福利一区二区三区| 性xxxxbbbb在线| 玖玖在线精品| 亚洲偷自偷白图片| 国产chinesetube| 女生扒开尿口让男生舔| 四虎影院网站| 国产精品国产主播在线观看| 天堂最新版在线地址| baoyu污污网站入口免费| 亚洲欧美色鬼久久综合|