C++標(biāo)準(zhǔn)之(ravalue reference) 右值引用介紹
臨時(shí)對(duì)象的產(chǎn)生和拷貝所帶來(lái)的效率折損,一直是C++所為人詬病的問(wèn)題。但是C++標(biāo)準(zhǔn)允許編譯器對(duì)于臨時(shí)對(duì)象的產(chǎn)生具有完全的自由度,從而發(fā)展出了CopyElision、RVO(包括NRVO)等編譯器優(yōu)化技術(shù),它們可以防止某些情況下臨時(shí)對(duì)象產(chǎn)生和拷貝。下面簡(jiǎn)單地介紹一下CopyElision、RVO,對(duì)此不感興趣的可以直接跳過(guò):
(1)CopyElision
CopyElision技術(shù)是為了防止某些不必要的臨時(shí)對(duì)象產(chǎn)生和拷貝,例如:
structA{
A(int){}
A(constA&){}
};
Aa=42;
理論上講,上述Aa=42;語(yǔ)句將分三步操作:第一步由42構(gòu)造一個(gè)A類型的臨時(shí)對(duì)象,第二步以臨時(shí)對(duì)象為參數(shù)拷貝構(gòu)造a,第三步析構(gòu)臨時(shí)對(duì)象。如果A是一個(gè)很大的類,那么它的臨時(shí)對(duì)象的構(gòu)造和析構(gòu)將造成很大的內(nèi)存開銷。我們只需要一個(gè)對(duì)象a,為什么不直接以42為參數(shù)直接構(gòu)造a呢?CopyElision技術(shù)正是做了這一優(yōu)化。
【說(shuō)明】:你可以在A的拷貝構(gòu)造函數(shù)中加一打印語(yǔ)句,看有沒(méi)有調(diào)用,如果沒(méi)有被調(diào)用,那么恭喜你,你的編譯器支持CopyElision。但是需要說(shuō)明的是:A的拷貝構(gòu)造函數(shù)雖然沒(méi)有被調(diào)用,但是它的實(shí)現(xiàn)不能沒(méi)有訪問(wèn)權(quán)限,不信你將它放在private權(quán)限里試試,編譯器肯定會(huì)報(bào)錯(cuò)。
(2)返回值優(yōu)化(RVO,ReturnValueOptimization)
返回值優(yōu)化技術(shù)也是為了防止某些不必要的臨時(shí)對(duì)象產(chǎn)生和拷貝,例如:
structA{
A(int){}
A(constA&){}
};
Aget(){returnA(1);}
Aa=get();
理論上講,上述Aa=get();語(yǔ)句將分別執(zhí)行:首先get()函數(shù)中創(chuàng)建臨時(shí)對(duì)象(假設(shè)為tmp1),然后以tmp1為參數(shù)拷貝構(gòu)造返回值(假設(shè)為tmp2),最后再以tmp2為參數(shù)拷貝構(gòu)造a,其中還伴隨著tmp1和tmp2的析構(gòu)。如果A是一個(gè)很大的類,那么它的臨時(shí)對(duì)象的構(gòu)造和析構(gòu)將造成很大的內(nèi)存開銷。返回值優(yōu)化技術(shù)正是用來(lái)解決此問(wèn)題的,它可以避免tmp1和tmp2兩個(gè)臨時(shí)對(duì)象的產(chǎn)生和拷貝。
【說(shuō)明】:a)你可以在A的拷貝構(gòu)造函數(shù)中加一打印語(yǔ)句,看有沒(méi)有調(diào)用,如果沒(méi)有被調(diào)用,那么恭喜你,你的編譯器支持返回值優(yōu)化。但是需要說(shuō)明的是:A的拷貝構(gòu)造函數(shù)雖然沒(méi)有被調(diào)用,但是它的實(shí)現(xiàn)不能沒(méi)有訪問(wèn)權(quán)限,不信你將它放在private權(quán)限里試試,編譯器肯定會(huì)報(bào)錯(cuò)。
b)除了返回值優(yōu)化,你可能還聽(tīng)說(shuō)過(guò)一個(gè)叫具名返回值優(yōu)化(NamedReturnValueOptimization,NRVO)的優(yōu)化技術(shù),從程序員的角度而言,它其實(shí)跟RVO同樣的邏輯。只是它的臨時(shí)對(duì)象具有變量名標(biāo)識(shí),例如修改上述get()函數(shù)為:
Aget(){
Atmp(1);//#1
//dosomething
returntmp;
}
Aa=get();//#2
想想上述修改后A類型共有幾次對(duì)象構(gòu)造?雖然#1處看起來(lái)有一次顯示地構(gòu)造,#2處看起來(lái)也有一次顯示地構(gòu)造,但如果你的編譯器支持NRVO和CopyElision,你會(huì)發(fā)現(xiàn)整個(gè)Aa=get();語(yǔ)句的執(zhí)行過(guò)程,只有一次A對(duì)象的構(gòu)造。如果你在get()函數(shù)return語(yǔ)句前打印tmp變量的地址,在Aa=get();語(yǔ)句后打印a的地址,你會(huì)發(fā)現(xiàn)兩者地址相同,這就是應(yīng)用了NRVO技術(shù)的結(jié)果。
(3)CopyElision、RVO無(wú)法避免的臨時(shí)對(duì)象的產(chǎn)生和拷貝
雖然CopyElision和NVO(包括NRVO)等技術(shù)能避免一些臨時(shí)對(duì)象的產(chǎn)生和拷貝,但某些情況下它們卻發(fā)揮不了作用,例如:
template<typenameT>
voidswap(T&a,T&b){
Ttmp(a);
a=b;
b=tmp;
}
我們只是想交換a和b兩個(gè)對(duì)象所擁有的數(shù)據(jù),但卻不得不使用一個(gè)臨時(shí)對(duì)象tmp備份其中一個(gè)對(duì)象,如果T類型對(duì)象擁有指向(或引用)從堆內(nèi)存分配的數(shù)據(jù),那么深拷貝所帶來(lái)的內(nèi)存開銷是可以想象的。為此,C++11標(biāo)準(zhǔn)引入了右值引用,使用它可以使臨時(shí)對(duì)象的拷貝具有move語(yǔ)意,從而可以使臨時(shí)對(duì)象的拷貝具有淺拷貝般的效率,這樣便可以從一定程度上解決臨時(shí)對(duì)象的深度拷貝所帶來(lái)的效率折損。
2、C++03標(biāo)準(zhǔn)中的左值與右值
要理解右值引用,首先得區(qū)分左值(lvalue)和右值(rvalue)。
C++03標(biāo)準(zhǔn)中將表達(dá)式分為左值和右值,并且“非左即右”:
Everyexpressioniseitheranlvalueoranrvalue.
區(qū)分一個(gè)表達(dá)式是左值還是右值,最簡(jiǎn)便的方法就是看能不能夠?qū)λ〉刂罚喝绻埽褪亲笾?;否則,就是右值。
【說(shuō)明】:由于右值引用的引入,C++11標(biāo)準(zhǔn)中對(duì)表達(dá)式的分類不再是“非左即右”那么簡(jiǎn)單,不過(guò)為了簡(jiǎn)單地理解,我們暫時(shí)只需區(qū)分左值右值即可,C++11標(biāo)準(zhǔn)中的分類后面會(huì)有描述。
3、右值引用的綁定規(guī)則
右值引用(rvaluereference,&&)跟傳統(tǒng)意義上的引用(reference,&)很相似,為了更好地區(qū)分它們倆,傳統(tǒng)意義上的引用又被稱為左值引用(lvaluereference)。下面簡(jiǎn)單地總結(jié)了左值引用和右值引用的綁定規(guī)則(函數(shù)類型對(duì)象會(huì)有所例外):
(1)非const左值引用只能綁定到非const左值;
(2)const左值引用可綁定到const左值、非const左值、const右值、非const右值;
(3)非const右值引用只能綁定到非const右值;
(4)const右值引用可綁定到const右值和非const右值。
測(cè)試?yán)尤缦拢?
structA{A(){}};
Alvalue;//非const左值對(duì)象
constAconst_lvalue;//const左值對(duì)象
Arvalue(){returnA();}//返回一個(gè)非const右值對(duì)象
constAconst_rvalue(){returnA();}//返回一個(gè)const右值對(duì)象
//規(guī)則一:非const左值引用只能綁定到非const左值
A&lvalue_reference1=lvalue;//ok
A&lvalue_reference2=const_lvalue;//error
A&lvalue_reference3=rvalue();//error
A&lvalue_reference4=const_rvalue();//error
//規(guī)則二:const左值引用可綁定到const左值、非const左值、const右值、非const右值
constA&const_lvalue_reference1=lvalue;//ok
constA&const_lvalue_reference2=const_lvalue;//ok
constA&const_lvalue_reference3=rvalue();//ok
constA&const_lvalue_reference4=const_rvalue();//ok
//規(guī)則三:非const右值引用只能綁定到非const右值
A&&rvalue_reference1=lvalue;//error
A&&rvalue_reference2=const_lvalue;//error
A&&rvalue_reference3=rvalue();//ok
A&&rvalue_reference4=const_rvalue();//error
//規(guī)則四:const右值引用可綁定到const右值和非const右值,不能綁定到左值
constA&&const_rvalue_reference1=lvalue;//error
constA&&const_rvalue_reference2=const_lvalue;//error
constA&&const_rvalue_reference3=rvalue();//ok
constA&&const_rvalue_reference4=const_rvalue();//ok
//規(guī)則五:函數(shù)類型例外
voidfun(){}
typedefdecltype(fun)FUN;//typedefvoidFUN();
FUN&lvalue_reference_to_fun=fun;//ok
constFUN&const_lvalue_reference_to_fun=fun;//ok
FUN&&rvalue_reference_to_fun=fun;//ok
constFUN&&const_rvalue_reference_to_fun=fun;//ok
【說(shuō)明】:(1)一些支持右值引用但版本較低的編譯器可能會(huì)允許右值引用綁定到左值,例如g++4.4.4就允許,但g++4.6.3就不允許了,clang++3.2也不允許,據(jù)說(shuō)VS2010beta版允許,正式版就不允許了,本人無(wú)VS2010環(huán)境,沒(méi)測(cè)試過(guò)。
(2)右值引用綁定到字面值常量同樣符合上述規(guī)則,例如:int&&rr=123;,這里的字面值123雖然被稱為常量,可它的類型為int,而不是constint。對(duì)此C++03標(biāo)準(zhǔn)文檔4.4.1節(jié)及其腳注中有如下說(shuō)明:
IfTisanon-classtype,thetypeofthervalueisthecv-unqualifiedversionofT.
InC++classrvaluescanhavecv-qualifiedtypes(becausetheyareobjects).ThisdiffersfromISOC,inwhichnon-lvaluesneverhavecv-qualifiedtypes.
因此123是非const右值,int&&rr=123;語(yǔ)句符合上述規(guī)則三。
4、C++11標(biāo)準(zhǔn)中的表達(dá)式分類
右值引用的引入,使得C++11標(biāo)準(zhǔn)中對(duì)表達(dá)式的分類不再是非左值即右值那么簡(jiǎn)單,下圖為C++11標(biāo)準(zhǔn)中對(duì)表達(dá)式的分類:

簡(jiǎn)單解釋如下:
(1)lvalue仍然是傳統(tǒng)意義上的左值;
(2)xvalue(eXpiringvalue)字面意思可理解為生命周期即將結(jié)束的值,它是某些涉及到右值引用的表達(dá)式的值(Anxvalueistheresultofcertainkindsofexpressionsinvolvingrvaluereferences),例如:調(diào)用一個(gè)返回類型為右值引用的函數(shù)的返回值就是xvalue。
(3)prvalue(purervalue)字面意思可理解為純右值,也可認(rèn)為是傳統(tǒng)意義上的右值,例如臨時(shí)對(duì)象和字面值等。
(4)glvalue(generalizedvalue)廣義的左值,包括傳統(tǒng)的左值和xvalue。
(5)rvalue除了傳統(tǒng)意義上的右值,還包括xvalue。
上述lvalue和prvalue分別跟傳統(tǒng)意義上的左值和右值概念一致,比較明確,而將xvalue描述為『某些涉及到右值引用的表達(dá)式的值』,某些是哪些呢?C++11標(biāo)準(zhǔn)給出了四種明確為xvalue的情況:
[Note:Anexpressionisanxvalueifitis:
--theresultofcallingafunction,whetherimplicitlyorexplicitly,whosereturntypeisanrvaluereferencetoobjecttype,
--acasttoanrvaluereferencetoobjecttype,
--aclassmemberaccessexpressiondesignatinganon-staticdatamemberofnon-referencetypeinwhichtheobjectexpressionisanxvalue,or
--a.*pointer-to-memberexpressioninwhichthefirstoperandisanxvalueandthesecondoperandisapointertodatamember.
Ingeneral,theeffectofthisruleisthatnamedrvaluereferencesaretreatedaslvaluesandunnamedrvaluereferencestoobjectsaretreatedasxvalues;rvaluereferencestofunctionsaretreatedaslvalueswhethernamedornot.--endnote]
[Example:
structA{
intm;
};
A&&operator+(A,A);
A&&f();
Aa;
A&&ar=static_cast<A&&>(a);
Theexpressionsf(),f().m,static_cast<A&&>(a),anda+aarexvalues.Theexpressionarisanlvalue.
--endexample]
簡(jiǎn)單地理解就是:具名的右值引用(namedrvaluereference)屬于左值,不具名的右值引用(unamedrvaluereference)就屬于xvalue,而引用函數(shù)類型的右值引用不論是否具名都當(dāng)做左值處理??磦€(gè)例子更容易理解:
[/code]
Arvalue(){returnA();}
A&&rvalue_reference(){returnA();}
fun();//返回的是不具名的右值引用,屬于xvalue
A&&ra1=rvalue();//ra1是具名右值應(yīng)用,屬于左值
A&&ra2=ra1;//error,ra1被當(dāng)做左值對(duì)待,因此ra2不能綁定到ra1(不符合規(guī)則三)
A&la=ra1;//ok,非const左值引用可綁定到非const左值(符合規(guī)則一)
5、move語(yǔ)意
現(xiàn)在,我們重新顧到1-(3),其中提到move語(yǔ)意,那么怎樣才能使臨時(shí)對(duì)象的拷貝具有move語(yǔ)意呢?下面我們以一個(gè)類的實(shí)現(xiàn)為例:
[code]
classA{
public:
A(constchar*pstr=0){m_data=(pstr!=0?strcpy(newchar[strlen(pstr)+1],pstr):0);}
//copyconstructor
A(constA&a){m_data=(a.m_data!=0?strcpy(newchar[strlen(a.m_data)+1],a.m_data):0);}
//copyassigment
A&operator=(constA&a){
if(this!=&a){
delete[]m_data;
m_data=(a.m_data!=0?strcpy(newchar[strlen(a.m_data)+1],a.m_data):0);
}
return*this;
}
//moveconstructor
A(A&&a):m_data(a.m_data){a.m_data=0;}
//moveassigment
A&operator=(A&&a){
if(this!=&a){
m_data=a.m_data;
a.m_data=0;
}
return*this;
}
~A(){delete[]m_data;}
private:
char*m_data;
};
從上例可以看到,除了傳統(tǒng)的拷貝構(gòu)造(copyconstructor)和拷貝賦值(copyassigment),我們還為A類的實(shí)現(xiàn)添加了移動(dòng)拷貝構(gòu)造(moveconstructor)和移動(dòng)賦值(moveassigment)。這樣,當(dāng)我們拷貝一個(gè)A類的(右值)臨時(shí)對(duì)象時(shí),就會(huì)使用具有move語(yǔ)意的移動(dòng)拷貝構(gòu)造函數(shù),從而避免深拷貝中strcpy()函數(shù)的調(diào)用;當(dāng)我們將一個(gè)A類的(右值)臨時(shí)對(duì)象賦值給另一個(gè)對(duì)象時(shí),就會(huì)使用具有move語(yǔ)意的移動(dòng)賦值,從而避免拷貝賦值中strcpy()函數(shù)的調(diào)用。這就是所謂的move語(yǔ)意。
6、std::move()函數(shù)的實(shí)現(xiàn)
了解了move語(yǔ)意,那么再來(lái)看1-(3)中的效率問(wèn)題:
template<typenameT>//如果T是classA
voidswap(T&a,T&b){
Ttmp(a);//根據(jù)右值引用的綁定規(guī)則三可知,這里不會(huì)調(diào)用moveconstructor,而會(huì)調(diào)用copyconstructor
a=b;//根據(jù)右值引用的綁定規(guī)則三可知,這里不會(huì)調(diào)用moveassigment,而會(huì)調(diào)用copyassigment
b=tmp;//根據(jù)右值引用的綁定規(guī)則三可知,這里不會(huì)調(diào)用moveassigment,而會(huì)調(diào)用copyassigment
}
從上例可以看到,雖然我們實(shí)現(xiàn)了moveconstructor和moveassigment,但是swap()函數(shù)的例子中仍然使用的是傳統(tǒng)的copyconstructor和copyassigment。要讓它們真正地使用move語(yǔ)意的拷貝和復(fù)制,就該std::move()函數(shù)登場(chǎng)了,看下面的例子:
voidswap(A&a,A&b){
Atmp(std::move(a));//std::move(a)為右值,這里會(huì)調(diào)用moveconstructor
a=std::move(b);//std::move(b)為右值,這里會(huì)調(diào)用moveassigment
b=std::move(tmp);//std::move(tmp)為右值,這里會(huì)調(diào)用moveassigment
}
我們不禁要問(wèn):我們通過(guò)右值應(yīng)用的綁定規(guī)則三和規(guī)則四,知道右值引用不能綁定到左值,可是std::move()函數(shù)是如何把上述的左值a、b和tmp變成右值的呢?這就要從std::move()函數(shù)的實(shí)現(xiàn)說(shuō)起,其實(shí)std::move()函數(shù)的實(shí)現(xiàn)非常地簡(jiǎn)單,下面以libcxx庫(kù)中的實(shí)現(xiàn)(在<type_trait>頭文件中)為例:
template<class_Tp>
inlinetypenameremove_reference<_Tp>::type&&move(_Tp&&__t){
typedeftypenameremove_reference<_Tp>::type_Up;
returnstatic_cast<_Up&&>(__t);
}
其中remove_reference的實(shí)現(xiàn)如下:
template<class_Tp>structremove_reference{typedef_Tptype;};
template<class_Tp>structremove_reference<_Tp&>{typedef_Tptype;};
template<class_Tp>structremove_reference<_Tp&&>{typedef_Tptype;};
從move()函數(shù)的實(shí)現(xiàn)可以看到,move()函數(shù)的形參(Parameter)類型為右值引用,它怎么能綁定到作為實(shí)參(Argument)的左值a、b和tmp呢?這不是仍然不符合右值應(yīng)用的綁定規(guī)則三嘛!簡(jiǎn)單地說(shuō),如果move只是個(gè)普通的函數(shù)(而不是模板函數(shù)),那么根據(jù)右值應(yīng)用的綁定規(guī)則三和規(guī)則四可知,它的確不能使用左值作為其實(shí)參。但它是個(gè)模板函數(shù),牽涉到模板參數(shù)推導(dǎo),就有所不同了。C++11標(biāo)準(zhǔn)文檔14.8.2.1節(jié)中,關(guān)于模板函數(shù)參數(shù)的推導(dǎo)描述如下:
Templateargumentdeductionisdonebycomparingeachfunctiontemplateparametertype(callitP)withthetypeofthecorrespondingargumentofthecall(callitA)asdescribedbelow.(14.8.2.1.1)
IfPisareferencetype,thetypereferredtobyPisusedfortypededuction.IfPisanrvaluereferencetoacvunqualifiedtemplateparameterandtheargumentisanlvalue,thetype"lvaluereferencetoA"isusedinplaceofAfortypededuction.(14.8.2.1.3)
大致意思是:模板參數(shù)的推導(dǎo)其實(shí)就是形參和實(shí)參的比較和匹配,如果形參是一個(gè)引用類型(如P&),那么就使用P來(lái)做類型推導(dǎo);如果形參是一個(gè)cv-unqualified(沒(méi)有const和volatile修飾的)右值引用類型(如P&&),并且實(shí)參是一個(gè)左值(如類型A的對(duì)象),就是用A&來(lái)做類型推導(dǎo)(使用A&代替A)。
template<class_Tp>voidf(_Tp&&){/*dosomething*/}
template<class_Tp>voidg(const_Tp&&){/*dosomething*/}
intx=123;
f(x);//ok,f()模板函數(shù)形參為非const非volatile右值引用類型,實(shí)參x為int類型左值,使用int&來(lái)做參數(shù)推導(dǎo),因此調(diào)用f<int&>(int&)
f(456);//ok,實(shí)參為右值,調(diào)用f<int>(int&&)
g(x);//error,g()函數(shù)模板參數(shù)為const右值引用類型,會(huì)調(diào)用g<int>(constint&&),通過(guò)右值引用規(guī)則四可知道,const右值引用不能綁定到左值,因此會(huì)導(dǎo)致編譯錯(cuò)誤
了解了模板函數(shù)參數(shù)的推導(dǎo)過(guò)程,已經(jīng)不難理解std::move()函數(shù)的實(shí)現(xiàn)了,當(dāng)使用左值(假設(shè)其類型為T)作為參數(shù)調(diào)用std::move()函數(shù)時(shí),實(shí)際實(shí)例化并調(diào)用的是std::move<T&>(T&),而其返回類型T&&,這就是move()函數(shù)左值變右值的過(guò)程(其實(shí)左值本身仍是左值,只是被當(dāng)做右值對(duì)待而已,被人“抄了家”,變得一無(wú)所有)。
【說(shuō)明】:C++的始祖BjarneStroustrup說(shuō):如果move()函數(shù)改名為rval()可能會(huì)更好些,但是move()這個(gè)名字已經(jīng)被使用了好些年了(Maybeitwouldhavebeenbetterifmove()hadbeencalledrval(),butbynowmove()hasbeenusedforyears.)。
7、完整的示例
至此,我們已經(jīng)了解了不少右值引用的知識(shí)點(diǎn)了,下面給出了一個(gè)完整地利用右值引用實(shí)現(xiàn)move語(yǔ)意的例子:
#include<iostream>
#include<cstring>
#definePRINT(msg)do{std::cout<<msg<<std::endl;}while(0)
template<class_Tp>structremove_reference{typedef_Tptype;};
template<class_Tp>structremove_reference<_Tp&>{typedef_Tptype;};
template<class_Tp>structremove_reference<_Tp&&>{typedef_Tptype;};
template<class_Tp>
inlinetypenameremove_reference<_Tp>::type&&move(_Tp&&__t){
typedeftypenameremove_reference<_Tp>::type_Up;
returnstatic_cast<_Up&&>(__t);
}
classA{
public:
A(constchar*pstr){
PRINT("constructor");
m_data=(pstr!=0?strcpy(newchar[strlen(pstr)+1],pstr):0);
}
A(constA&a){
PRINT("copyconstructor");
m_data=(a.m_data!=0?strcpy(newchar[strlen(a.m_data)+1],a.m_data):0);
}
A&operator=(constA&a){
PRINT("copyassigment");
if(this!=&a){
delete[]m_data;
m_data=(a.m_data!=0?strcpy(newchar[strlen(a.m_data)+1],a.m_data):0);
}
return*this;
}
A(A&&a):m_data(a.m_data){
PRINT("moveconstructor");
a.m_data=0;
}
A&operator=(A&&a){
PRINT("moveassigment");
if(this!=&a){
m_data=a.m_data;
a.m_data=0;
}
return*this;
}
~A(){PRINT("destructor");delete[]m_data;}
private:
char*m_data;
};
voidswap(A&a,A&b){
Atmp(move(a));
a=move(b);
b=move(tmp);
}
intmain(intargc,char**argv,char**env){
Aa("123"),b("456");
swap(a,b);
return0;
}
輸出結(jié)果為:
constructor
constructor
moveconstructor
moveassigment
moveassigment
destructor
destructor
destructor
8、幕后花絮
C++11標(biāo)準(zhǔn)引入右值引用的提案是由HowardHinnant提出的,它的最初提案N1377在02年就提出來(lái)了,中間經(jīng)歷了多次修改N1385、N1690、N1770、N1855、N1952、N2118。包括它的最終版本N2118在內(nèi),HowardHinnant的提案中都使用了右值引用直接綁定到左值的例子,并且由HowardHinnant、BjarneStroustrup和BronekKozicki三人08年10月共同署名的《ABriefIntroductiontoRvalueReferences》文章中也有右值引用直接綁定到左值的例子,但奇怪的是11年公布的最新的C++11標(biāo)準(zhǔn)文檔中卻不允許右值引用直接綁定到左值,其中的原因不得而知,但從中不難理解為什么早些編譯器版本(如g++4.4.4)對(duì)右值引用綁定到左值,不會(huì)報(bào)出編譯錯(cuò)誤,而最新的編譯器卻會(huì)報(bào)錯(cuò)。
另外,HowardHinnant是C++標(biāo)準(zhǔn)委員會(huì)LibraryWorkingGroup老大(chairman),libcxx和libcxxabi的維護(hù)者,蘋果公司的高級(jí)軟件工程師。
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)兩個(gè)遞減數(shù)列中尋找某一個(gè)數(shù)
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)兩個(gè)遞減數(shù)列中尋找某一個(gè)數(shù),是一類經(jīng)典的數(shù)組操作算法,需要的朋友可以參考下2014-09-09C++實(shí)現(xiàn)LeetCode(92.倒置鏈表之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(倒置鏈表之二),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07Qt實(shí)現(xiàn)一個(gè)簡(jiǎn)單的word文檔編輯器
本文主要介紹了Qt實(shí)現(xiàn)一個(gè)簡(jiǎn)單的word文檔編輯器,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07C++利用MySQL API連接和操作數(shù)據(jù)庫(kù)實(shí)例詳解
這篇文章主要介紹了C++利用MySQL API連接和操作數(shù)據(jù)庫(kù)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01C++實(shí)現(xiàn)歌手比賽評(píng)分系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)歌手比賽評(píng)分系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03實(shí)例講解C語(yǔ)言編程中的結(jié)構(gòu)體對(duì)齊
這篇文章主要介紹了C語(yǔ)言編程中的結(jié)構(gòu)體對(duì)齊,值得注意的是一些結(jié)構(gòu)體對(duì)齊的例子在不同編譯器下結(jié)果可能會(huì)不同,需要的朋友可以參考下2016-04-04詳解C++中的內(nèi)存同步模式(memory order)
這篇文章主要介紹了C++中的內(nèi)存同步模式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04C++超詳細(xì)講解貪心策略的設(shè)計(jì)及解決會(huì)場(chǎng)安排問(wèn)題
為了更好的應(yīng)對(duì)《算法設(shè)計(jì)與分析》這門課程,我把書上以及老師講過(guò)的案例都詳細(xì)的做一個(gè)重現(xiàn)及解剖,讓你熟記每一個(gè)潛在的考點(diǎn),希望能給大家?guī)椭?/div> 2022-05-05最新評(píng)論