C++是具有國(guó)際標(biāo)準(zhǔn)的編程語(yǔ)言,通常稱(chēng)作ANSI/ISO C++,1998年國(guó)際標(biāo)準(zhǔn)組織(ISO)頒布了C++語(yǔ)言的國(guó)際標(biāo)準(zhǔn)ISO/IEC 1488-1998。。1998年是C++標(biāo)準(zhǔn)委員會(huì)成立的第一年,以后每5年視實(shí)際需要更新一次標(biāo)準(zhǔn),最近一次標(biāo)準(zhǔn)更新是在2009年,目前我們一般稱(chēng)該標(biāo)準(zhǔn)C++0x。遺憾的是,由于C++語(yǔ)言過(guò)于復(fù)雜,以及他經(jīng)歷了長(zhǎng)年的演變,直到2009年只有Visual C++ 2010 CTP開(kāi)發(fā)環(huán)境的編譯器完全符合這個(gè)標(biāo)準(zhǔn)。C++委員會(huì)的主要焦點(diǎn)是在語(yǔ)言核心的發(fā)展上,C++0x關(guān)于核心語(yǔ)言的領(lǐng)域?qū)⒈淮蠓纳疲ǘ嗑€程支持、 泛型編程、統(tǒng)一的初始化,以及表現(xiàn)的加強(qiáng)。
成為國(guó)際標(biāo)準(zhǔn) C++0x最終國(guó)際投票已于2011年8月10日結(jié)束,所有國(guó)家都投出了贊成票。國(guó)際標(biāo)準(zhǔn)化組織 于 2011年9月1日出版發(fā)布ISO/IEC 14882:2011,名稱(chēng)是
Information technology -- Programming languages -- C++ Edition: 3
C++ 2011 正式成為新的C++標(biāo)準(zhǔn)
C++ 2011將取代現(xiàn)行的C++標(biāo)準(zhǔn)ISO/IEC 14882,它公開(kāi)于1998年并于2003年更新,通稱(chēng)C++98以及C++03。
新標(biāo)準(zhǔn)的pdf文檔可以在其iso官方網(wǎng)站購(gòu)買(mǎi)獲取。
C++委員會(huì)的主要焦點(diǎn)是在語(yǔ)言核心的發(fā)展上。因此C++0x的發(fā)表日期取決于這部份標(biāo)準(zhǔn)的作業(yè)進(jìn)度。
核心語(yǔ)言的領(lǐng)域?qū)⒈淮蠓纳?,包?a target="_blank" rel="nofollow" style="text-decoration: none; color: rgb(51, 102, 153); ">多線程支持、 泛型編程、統(tǒng)一的初始化,以及表現(xiàn)的加強(qiáng)。
在此分成4個(gè)區(qū)塊來(lái)討論核心語(yǔ)言的特色以及變更: 運(yùn)行期表現(xiàn)強(qiáng)化、建構(gòu)期表現(xiàn)強(qiáng)化、可用性強(qiáng)化,還有嶄新的機(jī)能。某些特色可能會(huì)同時(shí)屬于多個(gè)區(qū)塊,但在此僅于其最具代表性的區(qū)塊描述該特色。
2011年3月27日,IS0 C++ 委員會(huì)正式批準(zhǔn)了C++編程語(yǔ)言國(guó)際標(biāo)準(zhǔn)最終草案(FDIS)。標(biāo)準(zhǔn)本身已經(jīng)完成,接下來(lái)將是根據(jù)委員會(huì)會(huì)議修改意見(jiàn)更新工作草案,預(yù)計(jì)將用三周時(shí)間完成FDIS草案,然后交給日內(nèi)瓦的ITTF,最新的C++標(biāo)準(zhǔn)將在夏天發(fā)布,先前被臨時(shí)命名為C++0x的新標(biāo)準(zhǔn)將被稱(chēng)為C++ 2011。從2003年發(fā)布的C++03到2011年的C++ 2011,新標(biāo)準(zhǔn)的制定歷經(jīng)了8年時(shí)間。GCC和Visual C++編譯器都已加入了C++2011/C++0x的支持。
在舊標(biāo)準(zhǔn)C++語(yǔ)言中,臨時(shí)量(術(shù)語(yǔ)為右值,因其出現(xiàn)在賦值表達(dá)式的右邊)可以做參數(shù)傳給函數(shù),但只能被接受為const &類(lèi)型。這樣函數(shù)便無(wú)法區(qū)分傳給const &的是真正的右值還是普通const變量。而且,由于類(lèi)型為const &,函數(shù)也無(wú)法改變所傳對(duì)象的值。
C++0x將增加一種名為右值引用的新的引用類(lèi)型,記作typename &&。這種類(lèi)型可以被接受為非const值,從而允許改變其值。這種改變將允許某些對(duì)象創(chuàng)建轉(zhuǎn)移語(yǔ)義。
比如,一個(gè)std::vector,就其內(nèi)部實(shí)現(xiàn)而言,是一個(gè)C式數(shù)組的封裝。如果需要?jiǎng)?chuàng)建vector臨時(shí)量或者從函數(shù)中返回vector,那就只能通過(guò)創(chuàng)建一個(gè)新的vector并拷貝所有存于右值中的數(shù)據(jù)來(lái)存儲(chǔ)數(shù)據(jù)。之后這個(gè)臨時(shí)的vector則會(huì)被銷(xiāo)毀,同時(shí)刪除其包含的數(shù)據(jù)。
有了右值引用,一個(gè)參數(shù)為指向某個(gè)vector的右值引用的std::vector的轉(zhuǎn)移構(gòu)造器就能夠簡(jiǎn)單地將該右值中C式數(shù)組的指針復(fù)制到新的vector,然后將該右值清空。這里沒(méi)有數(shù)組拷貝,并且銷(xiāo)毀被清空的右值也不會(huì)銷(xiāo)毀保存數(shù)據(jù)的內(nèi)存。返回vector的函數(shù)現(xiàn)在只需要返回一個(gè)std::vector<>&&。如果vector沒(méi)有轉(zhuǎn)移構(gòu)造器,那么結(jié)果會(huì)像以前一樣:用std::vector<> &參數(shù)調(diào)用它的拷貝構(gòu)造器。如果vector確實(shí)具有轉(zhuǎn)移構(gòu)造器,那么轉(zhuǎn)移構(gòu)造器就會(huì)被調(diào)用,從而避免大量的內(nèi)存分配。
考慮到安全因素,具名變量即使被聲明為右值類(lèi)型也不會(huì)被當(dāng)作右值。如需把它當(dāng)作右值,須使用庫(kù)函數(shù)std::move()。
bool is_r_value(int &&)
{
return true;
}
bool is_r_value(const int &)
{
return false;
}
void test(int &&i)
{
is_r_value(i); // false
is_r_value(std::move(i)); // true
}
出于右值引用定義的本質(zhì)特征以及某些對(duì)左值引用(常規(guī)引用)定義的修改,現(xiàn)在右值引用允許程序員提供函數(shù)參數(shù)的完美轉(zhuǎn)發(fā)。當(dāng)與模板變參相結(jié)合時(shí),這種能力可以允許函數(shù)模板完美地將參數(shù)轉(zhuǎn)發(fā)給接受那些參數(shù)的其他函數(shù)。這在轉(zhuǎn)發(fā)構(gòu)造器參數(shù)時(shí)尤為有用:可以創(chuàng)建一些能自動(dòng)調(diào)用具有相應(yīng)參數(shù)構(gòu)造器的工廠函數(shù)。
C++語(yǔ)言一直具有常量表達(dá)式的概念。這些諸如3+4之類(lèi)的表達(dá)式總是產(chǎn)生相同的結(jié)果且不具備副作用。常量表達(dá)式給編譯器帶來(lái)了優(yōu)化的可能,而編譯器也經(jīng)常在編譯期執(zhí)行此類(lèi)表達(dá)式并將結(jié)果存放在程序中。此外,C++語(yǔ)言規(guī)范中有一些地方需要使用常量表達(dá)式。定義數(shù)組需要常量表達(dá)式,而枚舉值也必須是常量表達(dá)式。
然而,每當(dāng)碰到函數(shù)調(diào)用或?qū)ο髽?gòu)造,常量表達(dá)式便不再有效。所以簡(jiǎn)單如下例便不合法:
int GetFive()
{
return 5;
}
int some_value[GetFive() + 5]; //創(chuàng)建一個(gè)包含10個(gè)整型變量的數(shù)組,在標(biāo)準(zhǔn)C++中不合法
這段代碼在C++中不合法,因?yàn)镚etFive() + 5不是一個(gè)常量表達(dá)式。編譯器無(wú)從知曉GetFive在運(yùn)行期是否產(chǎn)生常量。理論上,這個(gè)函數(shù)可能會(huì)影響某個(gè)全局變量,或者調(diào)用其他運(yùn)行期產(chǎn)生非常量的函數(shù)。
C++0x將引入constexpr關(guān)鍵字,此關(guān)鍵字將使用戶能保證某個(gè)函數(shù)或構(gòu)造器在編譯期產(chǎn)生常量。上例可被改寫(xiě)如下:
constexpr int GetFive()
{
return 5;
}
int some_value[GetFive() + 5]; //在標(biāo)準(zhǔn)C++0x中合法
這段代碼將使編譯器理解并確認(rèn)GetFive是個(gè)編譯期常量。
在函數(shù)上使用constexpr將對(duì)函數(shù)功能施加嚴(yán)格的限制。首先,函數(shù)必須返回非void類(lèi)型。其次,函數(shù)體必須具有"return /expr/"的形式。第三,expr在參數(shù)替換后必須是常量表達(dá)式。該常量表達(dá)式只能調(diào)用其他定義為constexpr的函數(shù),只能使用其他常量表達(dá)式數(shù)據(jù)變量。第四,常量表達(dá)式中一切形式的遞歸均被禁止。最后,這種帶constexpr的函數(shù)在編譯單元中必須先定義后調(diào)用。
變量也可被定義為常量表達(dá)式值。
constexpr double forceOfGravity = 9.8;
constexpr double moonGravity = forceOfGravity / 6;
常量表達(dá)式數(shù)據(jù)變量隱含為常量。它們只能存放常量表達(dá)式或常量表達(dá)式構(gòu)造器的結(jié)果。
為了從用戶自定義類(lèi)型中構(gòu)建常量表達(dá)式數(shù)據(jù)值,構(gòu)造器在聲明時(shí)可帶constexpr。同常量表達(dá)式函數(shù)一樣,在編譯單元中常量表達(dá)式構(gòu)造器也必須先定義后使用。常量表達(dá)式構(gòu)造器函數(shù)體必須為空,而且它必須用常量表達(dá)式構(gòu)造其成員。這種類(lèi)型的析構(gòu)器必須是平凡的。
由常量表達(dá)式拷貝構(gòu)造的類(lèi)型也必須被定義為constexpr,以使它們能從常量表達(dá)式函數(shù)中作為值被返回。類(lèi)的任何成員函數(shù),包括拷貝構(gòu)造器和操作符重載,都能被聲明為constexpr,只要它們符合常量表達(dá)式函數(shù)的定義。這就允許編譯器在編譯期不僅能拷貝類(lèi)對(duì)象,也能對(duì)其實(shí)施其他操作。
常量表達(dá)式函數(shù)或構(gòu)造器可以用非constexpr參數(shù)來(lái)調(diào)用。就如同一個(gè)constexpr整數(shù)常量可以被賦給一個(gè)非constexpr變量一樣,constexpr函數(shù)也可用非constexpr參數(shù)來(lái)調(diào)用,并且其結(jié)果也可存放在非constexpr變量中。此關(guān)鍵字只是提供了在一個(gè)表達(dá)式的全部成員均為constexpr時(shí)其結(jié)果為編譯期常量的可能性。
在標(biāo)準(zhǔn)C++語(yǔ)言中,要讓結(jié)構(gòu)成為POD類(lèi)型必須滿足某幾條規(guī)則。有充分理由讓一大堆類(lèi)型滿足這些規(guī)則(定義);只要滿足這些規(guī)則,結(jié)構(gòu)的實(shí)現(xiàn)將產(chǎn)生兼容于C的對(duì)象布局。然而,在C++03中這些規(guī)則過(guò)于嚴(yán)格。注:POD,Plain Old Data,指POD用來(lái)表明C++中與C相兼容的數(shù)據(jù)類(lèi)型,可以按照C的方式來(lái)處理(運(yùn)算、拷貝等)。非POD數(shù)據(jù)類(lèi)型與C不兼容,只能按照C++特有的方式進(jìn)行使用。
C++0x將放松某些關(guān)于POD的限制規(guī)則。
如果一個(gè)類(lèi)或結(jié)構(gòu)是平凡的,具有標(biāo)準(zhǔn)布局的,且不包含任何非POD的非靜態(tài)成員,那么它就被認(rèn)定是POD。平凡的類(lèi)或結(jié)構(gòu)定義如下:
1.具有一個(gè)平凡的缺省構(gòu)造器。(可以使用缺省構(gòu)造器語(yǔ)法,如 SomeConstructor() = default;)
2.具有一個(gè)平凡的拷貝構(gòu)造器。(可以使用缺省構(gòu)造器語(yǔ)法)
3.具有一個(gè)平凡的拷貝賦值運(yùn)算符。(可以使用缺省語(yǔ)法)
4.具有一個(gè)非虛且平凡的析構(gòu)器。
一個(gè)具有標(biāo)準(zhǔn)布局的類(lèi)或結(jié)構(gòu)被定義如下:
1.所有非靜態(tài)數(shù)據(jù)成員均為標(biāo)準(zhǔn)布局類(lèi)型。
2.所有非靜態(tài)成員的訪問(wèn)權(quán)限(public, private, protected) 均相同。
3.沒(méi)有虛函數(shù)。
4.沒(méi)有虛基類(lèi)。
5.所有基類(lèi)均為標(biāo)準(zhǔn)布局類(lèi)型。
6.沒(méi)有任何基類(lèi)的類(lèi)型與類(lèi)中第一個(gè)非靜態(tài)成員相同。
7.要么全部基類(lèi)都沒(méi)有非靜態(tài)數(shù)據(jù)成員,要么最下層的子類(lèi)沒(méi)有非靜態(tài)數(shù)據(jù)成員且最多只有一個(gè)基類(lèi)有非靜態(tài)數(shù)據(jù)成員??傊^承樹(shù)中最多只能有一個(gè)類(lèi)有非靜態(tài)數(shù)據(jù)成員。所有非靜態(tài)數(shù)據(jù)成員必須都是標(biāo)準(zhǔn)布局類(lèi)型。
[ 附關(guān)于POD的說(shuō)明:]
PLAIN OLD DATA
plain old data 的縮寫(xiě)(POD)一個(gè)普通的古老的數(shù)據(jù)結(jié)構(gòu)(POD)是一種數(shù)據(jù)結(jié)構(gòu)。它僅作為被動(dòng)的收藏的字段值,不使用封包或者otherobject-oriented特征。(A plain old data structure(POD) is a data structurethat is represented only as passive collections of field values, without using encapsulationor otherobject-orientedfeatures.)
在C++中,我們把傳統(tǒng)的C風(fēng)格的struct叫做POD(Plain Old Data)對(duì)象。一般來(lái)說(shuō),POD對(duì)象應(yīng)該滿足如下特性。
對(duì)于POD類(lèi)型T的對(duì)象,不管這個(gè)對(duì)象是否擁有類(lèi)型T的有效值,如果將該對(duì)象的底層字節(jié)序列復(fù)制到一個(gè)字符數(shù)組(或者無(wú)符號(hào)字符數(shù)組)中,再將其復(fù)制回對(duì)象,那么該對(duì)象的值與原始值一樣??荚嚲偷?/p>
對(duì)于任意的POD類(lèi)型T,如果兩個(gè)T指針?lè)謩e指向兩個(gè)不同的對(duì)象obj1和obj2,如果用memcpy庫(kù)函數(shù)把obj1的值復(fù)制到obj2,那么obj2將擁有與obj1相同的值。
簡(jiǎn)言之,針對(duì)POD對(duì)象,其二進(jìn)制內(nèi)容是可以隨便復(fù)制的,在任何地方,只要其二進(jìn)制內(nèi)容在,就能還原出正確無(wú)誤的POD對(duì)象。對(duì)于任何POD對(duì)象,都可以使用memset()函數(shù)或者其他類(lèi)似的內(nèi)存初始化函數(shù)。
在標(biāo)準(zhǔn)C++語(yǔ)言中,如果在某一個(gè)編譯單元中編譯器碰到一個(gè)參數(shù)完全指定的模板,它就必須實(shí)例化該模板。這種做法可能大大延長(zhǎng)編譯時(shí)間,尤其在許多編譯單元使用同樣的參數(shù)實(shí)例化該模板時(shí)。
C++0x將引入外部模板的概念。C++已經(jīng)擁有了迫使編譯器在某一地點(diǎn)實(shí)例化模板的語(yǔ)法:
template class std::vector<MyClass>;
C++所缺乏的是防止編譯器具現(xiàn)化某個(gè)模板的能力。C++0x只是簡(jiǎn)單地將語(yǔ)法擴(kuò)展為:
extern template class std::vector<MyClass>;
這段代碼將告訴編譯器不要在這個(gè)編譯單元實(shí)例化此模板。
標(biāo)準(zhǔn)C++語(yǔ)言從C語(yǔ)言中借入了初始化列表概念。根據(jù)這一概念,結(jié)構(gòu)或數(shù)組可以通過(guò)給定一串按照結(jié)構(gòu)中成員定義的次序排列的參數(shù)來(lái)創(chuàng)建。初始化列表可以遞歸創(chuàng)建,因此結(jié)構(gòu)數(shù)組或包含其他結(jié)構(gòu)的結(jié)構(gòu)也能使用初始化列表。這對(duì)于靜態(tài)列表或用某些特定值初始化結(jié)構(gòu)而言非常有用。C++語(yǔ)言中存在能讓對(duì)象初始化的構(gòu)造器特性。但構(gòu)造器特性本身并不能取代初始化列表的所有功能。標(biāo)準(zhǔn)C++允許類(lèi)和結(jié)構(gòu)使用初始化列表,但它們必須滿足POD的定義。非POD的類(lèi)不能使用初始化列表,一些C++式的容器如std::vector和boost::array也不行。
C++0x將把初始化列表綁定為一種名為std::initializer_list的類(lèi)型。這將允許構(gòu)造器及其他函數(shù)接受初始化列表作為其參數(shù)。比如:
class SequenceClass
{
public:
SequenceClass(std::initializer_list < int >list);
};
這段代碼將允許SequenceClass用一串整數(shù)構(gòu)造,如下所示:
SequenceClass someVar = {1, 4, 5, 6};
這種構(gòu)造器是一種特殊類(lèi)型的構(gòu)造器,名為初始化列表構(gòu)造器。具有這種構(gòu)造器的類(lèi)在統(tǒng)一的初始化形式中將被特殊對(duì)待。
std::initializer_list<>類(lèi)在C++0x標(biāo)準(zhǔn)庫(kù)中將成為一等公民。但是這個(gè)類(lèi)的對(duì)象只能通過(guò)使用{}語(yǔ)法由C++0x編譯器靜態(tài)構(gòu)建并初始化。列表一旦構(gòu)建即可被拷貝,盡管只是引用拷貝。初始化列表是常量,一旦構(gòu)建,組成列表的成員以及其成員所包含的數(shù)據(jù)便無(wú)法改變。
由于初始化列表是一種真實(shí)的類(lèi)型,因此在類(lèi)構(gòu)造器之外的地方也能使用。常規(guī)函數(shù)也可接受初始化列表作為其參數(shù)。比如:
void FunctionName(std::initializer_list<float> list);
FunctionName({1.0f, -3.45f, -0.4f});
另外,標(biāo)準(zhǔn)容器也可用初始化列表初始化。比如:
vector<string> DayOfWeek={"Monday", "Tuesday", "Wednesday"};
標(biāo)準(zhǔn)C++在類(lèi)型初始化中存在一些問(wèn)題。語(yǔ)言中存在幾種類(lèi)型初始化方式,但替換使用的話產(chǎn)生的結(jié)果不盡相同。傳統(tǒng)的構(gòu)造語(yǔ)法看起來(lái)更像函數(shù)聲明。必須采取措施以使編譯器不把對(duì)象構(gòu)造誤認(rèn)為函數(shù)聲明。只有集合類(lèi)型和POD類(lèi)型能用集合初始化器初始化(用SomeType var = {/*stuff*/};).
C++0x將提供一種能作用于任何對(duì)象的完全統(tǒng)一的類(lèi)型初始化形式。這種形式對(duì)初始化列表語(yǔ)法作了擴(kuò)展:
struct BasicStruct
{
int x;
float y;
};
struct AltStruct
{
AltStruct(int _x, float _y) : x(_x), y(_y) {}
private:
int x;
float y;
};
BasicStruct var1{5, 3.2f};
AltStruct var2{2, 4.3f};
var1的初始化的運(yùn)作方式就如同一個(gè)C式的初始化列表。每個(gè)public變量都將用初始化列表中的值初始化。如果需要,隱式類(lèi)型轉(zhuǎn)化將被使用,并且如果沒(méi)有隱式類(lèi)型轉(zhuǎn)化可供使用,編譯器將報(bào)告編譯失敗。
var2的初始化只是簡(jiǎn)單地調(diào)用構(gòu)造器。
統(tǒng)一的初始化對(duì)象構(gòu)造將消除在某些情況下指定類(lèi)型的需要:
struct IdString
{
std::string name;
int identifier;
};
IdString var3{"SomeName", 4};
這種語(yǔ)法會(huì)自動(dòng)使用const char *調(diào)用std::string進(jìn)行初始化。程序員也可以使用下面的代碼:
IdString GetString()
{
return {"SomeName", 4}; //注意,這里沒(méi)寫(xiě)return ldString{"SomeName", 4}
}
統(tǒng)一的初始化形式不會(huì)取代構(gòu)造器語(yǔ)法。某些情況下仍然需要構(gòu)造器語(yǔ)法。如果一個(gè)類(lèi)具有初始化列表構(gòu)造器(TypeName(initializer_list);),,那么只要初始化列表符合該構(gòu)造器的類(lèi)型,初始化列表構(gòu)造將優(yōu)先于其他構(gòu)造形式。
C++0x版本的std::vector將擁有匹配與模板參數(shù)的初始化列表構(gòu)造器。這就意味著下面這段代碼:
std::vector<int> theVec{4};
這段代碼將調(diào)用初始化列表構(gòu)造器,而不會(huì)調(diào)用std::vector中接受單個(gè)長(zhǎng)度參數(shù)并創(chuàng)建相應(yīng)長(zhǎng)度的vector的構(gòu)造器。為了調(diào)用后一個(gè)構(gòu)造器,用戶需要直接使用標(biāo)準(zhǔn)構(gòu)造器語(yǔ)法。
在標(biāo)準(zhǔn)的C++和C語(yǔ)言中,變量在使用時(shí)必須明確指定其類(lèi)型。然而,隨著模板類(lèi)型及模板元編程的到來(lái),表述某些定義完好的函數(shù)的返回值的類(lèi)型變得不那么容易了。由此,在函數(shù)中存儲(chǔ)中間值也變得困難,用戶有可能需要了解某個(gè)模板元編程庫(kù)的內(nèi)部結(jié)構(gòu)才行。
C++0x將通過(guò)兩種方式來(lái)緩解這些困難。首先,帶有明確初始化的變量定義將可以使用auto關(guān)鍵字。這種初始化將創(chuàng)建與初始化器類(lèi)型相同的變量。
auto someStrangeCallableType = boost::bind(&SomeFunction, _2, _1, someObject);
auto otherVariable = 5;
someStrangeCallableType的類(lèi)型將等同于任何由boost::bind所返回的適合于這些特定參數(shù)的模板函數(shù)的類(lèi)型。編譯器很容易知道其類(lèi)型,用戶則不然。
otherVariable的類(lèi)型也定義完好,但用戶更容易推定其類(lèi)型。該變量是整型,也就是整型常量的類(lèi)型。
另外,關(guān)鍵字decltype可用于在編譯期確定某個(gè)表達(dá)式的類(lèi)型。比如:
int someInt;
decltype(someInt) otherIntegerVariable = 5;
這種用法相對(duì)于auto可能更有效,因?yàn)閍uto變量的類(lèi)型只有編譯器才知道。而且,對(duì)于那些大量使用操作符重載及特化類(lèi)型的代碼,使用decltype來(lái)推導(dǎo)表達(dá)式的類(lèi)型也很有用。
auto在減少代碼冗余性方面也很有用。比如,寫(xiě)下面這段代碼時(shí):
for(vector<int>::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr)
程序員可以使用下面這種更短的形式:
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)
當(dāng)程序員在開(kāi)始使用嵌套容器時(shí),這兩段代碼的區(qū)別將更加明顯,盡管在這種情況下使用typedef也是一種減少代碼的好方法。
C++庫(kù)Boost定義了幾個(gè)區(qū)間概念。區(qū)間代表了與容器相類(lèi)似的列表中兩點(diǎn)之間的可控列表。已序容器是區(qū)間的超集。已序容器中的兩個(gè)迭代器也能定義一個(gè)區(qū)間。這些概念和算法都將被融入C++0x的標(biāo)準(zhǔn)庫(kù)中。然而,C++0x還將提供一種專(zhuān)用的語(yǔ)言設(shè)施來(lái)運(yùn)用區(qū)間概念。
for語(yǔ)句將使區(qū)間概念上的循環(huán)更容易:
int my_array[5] = { 1, 2, 3, 4, 5 };
for(int &x: my_array)
{
x *= 2;
}
新的for循環(huán)的第一部分定義了用于在區(qū)間上循環(huán)的變量。和普通for循環(huán)中聲明的變量一樣,該變量的作用域也僅限于循環(huán)之內(nèi)。置于":"之后的第二部分則表示將進(jìn)行循環(huán)的區(qū)間。在這種情況下,存在一個(gè)約束映射可以將C式數(shù)組轉(zhuǎn)化為區(qū)間。
進(jìn)行循環(huán)的區(qū)間還可以是std::vector,或任何符合區(qū)間概念的對(duì)象。
在標(biāo)準(zhǔn)C++語(yǔ)言中,尤其在使用諸如sort和find之類(lèi)的標(biāo)準(zhǔn)庫(kù)算法函數(shù)時(shí),用戶總是希望在算法函數(shù)調(diào)用的觸發(fā)點(diǎn)附近定義謂詞函數(shù)。在這一方面語(yǔ)言中只有一種機(jī)制可供利用:在函數(shù)中定義類(lèi)。通常這種做法既啰嗦又笨重。另外,標(biāo)準(zhǔn)C++語(yǔ)言不允許在函數(shù)中定義的類(lèi)充當(dāng)模板參數(shù),所以這種做法行不通。
顯而易見(jiàn),解決方案在于允許定義lambda表達(dá)式和 lambda函數(shù)。C++0x將允許定義lambda函數(shù)。
lambda函數(shù)可以定義如下:
[](int x, int y) { return x + y }
此無(wú)名函數(shù)的返回值類(lèi)型為decltype(x+y)。只有l(wèi)ambda函數(shù)的形式為"return /expression/"時(shí),返回值類(lèi)型才能省略。因此這種lambda函數(shù)內(nèi)部只能有一句語(yǔ)句。
返回值類(lèi)型也可像下例那樣明確指定。一個(gè)更為復(fù)雜的例子:
[](int x, int y) -> int { int z = x + y; return z + x; }
在此例中,臨時(shí)變量z被創(chuàng)建并用于存儲(chǔ)中間值。和普通函數(shù)一樣,中間值在多次函數(shù)調(diào)用之間不會(huì)被保存。
如果lambda函數(shù)不返回值,即返回值類(lèi)型為void的話,該返回值類(lèi)型也可完全省略。
在lambda函數(shù)作用域范圍內(nèi)被定義的變量的引用也能被使用。這些變量的合集通常被稱(chēng)為閉包。閉包可以定義并使用如下:
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&total](int x) {
total += x
});
std::cout << total;
這段代碼將顯示列表中所有元素的總和。變量total將被存為該lambda函數(shù)相應(yīng)的閉包的一部分。由于閉包變量total是棧變量total的引用,使用前者可以改變后者的值。
為棧變量生成的閉包變量也可以不用引用操作符/&/定義,這種情況下lambda函數(shù)將拷貝其值。這種做法將促使用戶明確聲明其意圖:是引用棧變量還是拷貝棧變量。引用棧變量可能會(huì)產(chǎn)生危險(xiǎn)。如果某個(gè)lambda函數(shù)將在所創(chuàng)建的作用域之外被引用(比如將此lambda函數(shù)存放在std::function(C++0x標(biāo)準(zhǔn))對(duì)象中可以做到這一點(diǎn)),那么用戶必須保證該lambda函數(shù)沒(méi)有引用任何棧變量。
對(duì)于那些可以保證只在其所創(chuàng)建的作用域內(nèi)被執(zhí)行的lambda函數(shù),使用棧變量無(wú)須通過(guò)顯式引用:
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&](int x) {
total += x
});
這種lambda函數(shù)的具體內(nèi)部實(shí)現(xiàn)可能會(huì)有所不同,但可以預(yù)期這種lambda函數(shù)可能不會(huì)保存所有棧變量的引用而是會(huì)保存函數(shù)創(chuàng)建時(shí)的棧指針。
如果不用[&]而用[=],那么所有被引用的變量都將被拷貝,從而允許lambda函數(shù)在原有變量生命期結(jié)束后仍然能夠被使用。
缺省指示符還能和參數(shù)列表結(jié)合使用。比如,如果用戶希望只拷貝其中一個(gè)變量的值,而對(duì)其他變量使用引用,則可使用下面的代碼:
int total = 0;
int value = 5;
[&, value](int x) { total += (x * value) };
這段代碼將導(dǎo)致total被存為引用,而value則會(huì)被存為拷貝。
如果一個(gè)lambda函數(shù)由一個(gè)類(lèi)的某個(gè)成員函數(shù)定義,那么此lambda函數(shù)便被認(rèn)定為該類(lèi)的友元。這種lambda函數(shù)可以使用屬于該類(lèi)類(lèi)型的對(duì)象的引用并訪問(wèn)其內(nèi)部成員。
[](SomeType *typePtr) { typePtr->SomePrivateMemberFunction() };
只有當(dāng)lambda函數(shù)在SomeType的某個(gè)成員函數(shù)中創(chuàng)建時(shí)這段代碼才能工作。
對(duì)于指向當(dāng)前成員函數(shù)所隸屬對(duì)象的this指針,其處理有些特殊:必須在lambda函數(shù)中明確指定。
[this]() { this->SomePrivateMemberFunction() };
使用[&] 或 [=]形式將使this自動(dòng)可用。
Lambda函數(shù)是一些類(lèi)型取決于編譯器的函數(shù)對(duì)象。它們的類(lèi)型只對(duì)編譯器開(kāi)放。如果用戶希望把lambda函數(shù)當(dāng)作參數(shù),那么要么參數(shù)相應(yīng)類(lèi)型為模板,要么創(chuàng)建一個(gè)std::function用于保存lambda函數(shù)。使用auto關(guān)鍵字則可以將lambda函數(shù)保存在局部變量中。
auto myLambdaFunc = [this]() {
this->SomePrivateMemberFunction()
};
然而,如果lambda函數(shù)的所有閉包變量均為引用,或者lambda函數(shù)根本沒(méi)有閉包變量,那么所產(chǎn)生的函數(shù)對(duì)象將具有一種特殊類(lèi)型:std::reference_closure。其中R(P)是帶返回值的函數(shù)簽名。這樣做的理由在于期望此種類(lèi)型的效率能好于使用std::function。
std::reference_closure<void()> myLambdaFunc = [this]() {
this->SomePrivateMemberFunction()
};
myLambdaFunc();
標(biāo)準(zhǔn)C語(yǔ)言的函數(shù)聲明語(yǔ)法對(duì)于C語(yǔ)言的特性集來(lái)說(shuō)是完美無(wú)缺的。由于C++語(yǔ)言演化自C語(yǔ)言,C++語(yǔ)言保留了相關(guān)的基本語(yǔ)法并在需要時(shí)進(jìn)行擴(kuò)充。然而,當(dāng)C++變得更為復(fù)雜時(shí),這種語(yǔ)法也暴露了一些局限性,尤其是在模板函數(shù)聲明中。比如,以下代碼在C++03中不合法:
template< typename LHS, typename RHS>
Ret AddingFunc(const LHS &lhs, const RHS &rhs)
{
return lhs + rhs;
}
類(lèi)型Ret為任何LHS和RHS相加所產(chǎn)生的類(lèi)型。即使有了前面所講述的C++0x的decltype功能,仍然不行:
template< typename LHS, typename RHS>
decltype(lhs+rhs) AddingFunc(const LHS &lhs, const RHS &rhs)
{
return lhs + rhs;
}
這一段并非合法的C++代碼,因?yàn)閘hs和rhs尚未定義,只有在詞法分析器分析出函數(shù)原型的其余部分之后這兩者才能成為有效的標(biāo)識(shí)符。
為解決這一問(wèn)題,C++0x將引入一種新型的函數(shù)定義和聲明的語(yǔ)法:
template < typename LHS, typename RHS >
auto AddingFunc(const LHS & lhs, const RHS & rhs)->decltype(lhs + rhs)
{
return lhs + rhs;
}
這一語(yǔ)法也能用于更為平常的函數(shù)聲明和定義中:
struct SomeStruct
{
auto FuncName(int x, int y)->int;
};
auto SomeStruct::FuncName(int x, int y)->int
{
return x + y;
}
在C++語(yǔ)言中,模板類(lèi)和模板函數(shù)必須對(duì)它們所接受的類(lèi)型施加某些限制。比如,STL容器要求容器中的類(lèi)型必須可以賦值。與類(lèi)繼承所展示的動(dòng)多態(tài)(任何能接受Foo&類(lèi)型對(duì)象作為參數(shù)的函數(shù)也能傳入Foo的子類(lèi)型)有所不同,任何類(lèi)只要支持某個(gè)模板所使用的操作,它就能被用于該模板。在函數(shù)傳參數(shù)的情況下,參數(shù)所必須滿足的需求是清晰的(必須是Foo的子類(lèi)型),而模板的場(chǎng)合下,對(duì)象所需滿足的接口則是隱含在模板實(shí)現(xiàn)當(dāng)中的。約束則提供了一種將模板參數(shù)所必需滿足的接口代碼化的機(jī)制。
引入約束的最初動(dòng)因在于改進(jìn)編譯錯(cuò)誤信息的質(zhì)量。如果程序員試圖使用一種不能提供某個(gè)模板所需接口的類(lèi)型,那么編譯器將產(chǎn)生錯(cuò)誤信息。然而,這些錯(cuò)誤信息通常難以理解,尤其對(duì)于新手而言。首先,錯(cuò)誤信息中的模板參數(shù)通常被完整拼寫(xiě)出來(lái),這將導(dǎo)致異常龐大的錯(cuò)誤信息。在某些編譯器上,簡(jiǎn)單的錯(cuò)誤會(huì)產(chǎn)生好幾K的錯(cuò)誤信息。其次,這些錯(cuò)誤信息通常不會(huì)指向錯(cuò)誤的實(shí)際發(fā)生地點(diǎn)。比如,如果程序員試圖創(chuàng)建一個(gè)其成員為不具備拷貝構(gòu)造器對(duì)象的vector,首先出現(xiàn)的錯(cuò)誤信息幾乎總是指向vector類(lèi)中試圖拷貝構(gòu)造其成員的那段代碼。程序員必須具備足夠的經(jīng)驗(yàn)和能力才能判斷出實(shí)際的錯(cuò)誤在于相應(yīng)類(lèi)型無(wú)法完全滿足vector所需要的接口。
在試圖解決此問(wèn)題的過(guò)程中,C++0x為語(yǔ)言添加了約束這一特性。與OOP使用基類(lèi)來(lái)限制類(lèi)型的功能相似,約束是一種限制類(lèi)型接口的具名結(jié)構(gòu)。而與OOP所不同的是,約束定義并非總是與傳入模板的參數(shù)類(lèi)型明確相關(guān),但它總是與模板定義相關(guān):
template < LessThanComparable T >
const T & min(const T & x, const T & y)
{
return y < x ? y : x;
}
這里沒(méi)有用/class /或/ typename/將模板參數(shù)指定為任意類(lèi)型,而是使用了/LessThanComparable/這個(gè)之前定義的約束。如果某個(gè)傳入/min/模板參數(shù)的類(lèi)型不符合/LessThanComparable/約束的定義,那么編譯器將報(bào)告編譯錯(cuò)誤,告訴用戶用來(lái)具現(xiàn)化該模板的類(lèi)型不符合/LessThanComparable/約束。
下面是一個(gè)更一般化的約束形式:
template<typename T> requires LessThanComparable<T>
const T& min(const T &x, const T &y)
{
return y < x ? y : x;
}
關(guān)鍵字/requires/之后為一串約束的聲明。它可以被用于表述涉及多個(gè)類(lèi)型的約束。此外,如果用戶希望當(dāng)類(lèi)型匹配該約束時(shí)不要使用某個(gè)特定模板,也可以用/requires !LessThanComparable/??梢韵衲0逄鼗菢邮褂眠@種機(jī)制。一個(gè)通用模板可能通過(guò)顯式禁用一些特性豐富的約束來(lái)處理具有較少特性的類(lèi)型。而這些約束則可通過(guò)特化利用某些特性來(lái)取得更高的效率并實(shí)現(xiàn)更多的功能。
約束定義如下:
auto concept LessThanComparable < typename T >
{
bool operator<(T, T);
}
這個(gè)例子中的關(guān)鍵字/auto/意味著任何類(lèi)型只要支持約束中所指定的操作便被認(rèn)定支持該約束。如果不使用/auto/關(guān)鍵字,為聲明某個(gè)類(lèi)型支持該約束就必須對(duì)該類(lèi)型使用約束映射。
該約束聲明任何類(lèi)型只要定義了接受兩個(gè)參數(shù)并返回bool型的<操作符就被認(rèn)為是/LessThanComparable/。該操作符不一定是一個(gè)自由函數(shù),它也可以是T類(lèi)型的成員函數(shù)。
約束也可以涉及多個(gè)類(lèi)型。比如,約束能表示一個(gè)類(lèi)型可以轉(zhuǎn)換為另一個(gè)類(lèi)型:
auto concept Convertible < typename T, typename U >
{
operator U(const T &);
}
為了在模板中使用這個(gè)約束,模板必須使用一種更為一般化的形式:
template < typename U, typename T >
requires Convertible < T, U > U convert(const T & t)
{
return t;
}
約束可以組合運(yùn)用。比如,給定一個(gè)名為/Regular/的約束
concept InputIterator < typename Iter, typename Value >
{
requires Regular < Iter >;
Value operator*(const Iter &);
Iter & operator++(Iter &);
Iter operator++(Iter &, int);
}
/InputIterator/約束的第一個(gè)模板參數(shù)必須符合/Regular/約束。
與繼承相似,約束也可派生自另一約束。與類(lèi)繼承相似,滿足派生約束所有限制條件的類(lèi)型必須滿足基本約束的所有限制條件。約束派生定義形同類(lèi)派生:
concept ForwardIterator<typename Iter, typename Value> :
InputIterator<Iter, Value>
{
//Add other requirements here.
}
類(lèi)型名可以與約束相關(guān)。這將施加一些限制條件:在使用這些約束的模板中,這些類(lèi)型名可供使用:
concept InputIterator < typename Iter >
{
typename value_type;
typename reference;
typename pointer;
typename difference_type;
requires Regular < Iter >;
requires Convertible < reference, value_type >;
reference operator*(const Iter &); // dereference
Iter & operator++(Iter &); // pre-increment
Iter operator++(Iter &, int); // post-increment
// ...
}
約束映射允許某些類(lèi)型被顯式綁定到某個(gè)約束。如有可能,約束映射也允許在不改變類(lèi)型定義的前提下讓該類(lèi)型采用某個(gè)約束的語(yǔ)法。比如下例:
concept_map InputIterator < char *>
{
typedef char value_type;
typedef char &reference;
typedef char *pointer;
typedef std::ptrdiff_t difference_type;
};
這個(gè)約束映射填補(bǔ)了當(dāng)/InputIterator/映射作用于/char*/類(lèi)型時(shí)所需要的類(lèi)型名。
為增加靈活性,約束映射本身也能被模板化。上例可以被延伸至所有指針類(lèi)型:
template < typename T > concept_map InputIterator < T * >
{
typedef T value_type;
typedef T & reference;
typedef T *pointer;
typedef std::ptrdiff_t difference_type;
};
此外,約束映射也可充當(dāng)迷你類(lèi)型,此時(shí)它會(huì)包含函數(shù)定義以及其他與類(lèi)相關(guān)的結(jié)
構(gòu)設(shè)施:
concept Stack < typename X >
{
typename value_type;
void push(X &, const value_type &);
void pop(X &);
value_type top(const X &);
bool empty(const X &);
};
template < typename T > concept_map Stack < std::vector < T > >
{
typedef T value_type;
void push(std::vector < T > &v, const T & x)
{
v.push_back(x);
}
void pop(std::vector < T > &v)
{
v.pop_back();
}
T top(const std::vector < T > &v)
{
return v.back();
}
bool empty(const std::vector < T > &v)
{
return v.empty();
}
};
這個(gè)約束映射將允許任何接受實(shí)現(xiàn)了/Stack/約束的類(lèi)型的模板也接受
/std::vector/,同時(shí)將所有函數(shù)調(diào)用映射為對(duì)/std::vector/的調(diào)用。最終,這種
做法將允許一個(gè)已經(jīng)存在的對(duì)象在不改變其定義的前提下,轉(zhuǎn)換其接口并為模板函
數(shù)所利用。
最后需要指出的是,某些限制條件可以通過(guò)靜態(tài)斷言來(lái)檢測(cè)。這種手段可以用來(lái)檢
測(cè)那些模板需要但卻面向其他方面問(wèn)題的限制條件。
在標(biāo)準(zhǔn)C++語(yǔ)言中,構(gòu)造器不能調(diào)用其他構(gòu)造器。每個(gè)構(gòu)造器要么獨(dú)自構(gòu)建類(lèi)的所有成員要么調(diào)用某個(gè)公共成員函數(shù)?;?lèi)的構(gòu)造器不能直接暴露給派生類(lèi):即便基類(lèi)的構(gòu)造器更合適,子類(lèi)也必須實(shí)現(xiàn)自己的構(gòu)造器。類(lèi)的非靜態(tài)數(shù)據(jù)成員不能在其聲明的場(chǎng)所初始化,它們只能在構(gòu)造器中初始化。
C++0x將為所有這些問(wèn)題提供解決方案。
C++0x將允許構(gòu)造器調(diào)用其他伙伴構(gòu)造器(被稱(chēng)為委托)。如此,只需添加少量代碼,構(gòu)造器便能利用其他構(gòu)造器的行為。另外一些語(yǔ)言,比如Java和C#,允許這樣做。語(yǔ)法如下:
class SomeType
{
int number;
public:
SomeType(int newNumber):number(newNumber) {}
SomeType():SomeType(42) {}
};
這就產(chǎn)生了一個(gè)問(wèn)題:C++03認(rèn)為一個(gè)對(duì)象在其構(gòu)造器執(zhí)行完畢時(shí)才能構(gòu)建完成,而C++0x則認(rèn)為一個(gè)對(duì)象在任何構(gòu)造器執(zhí)行完畢時(shí)都將構(gòu)建完成。由于多個(gè)構(gòu)造器被允許執(zhí)行,這將導(dǎo)致每個(gè)委托構(gòu)造器都可能在一個(gè)已經(jīng)構(gòu)造完成的對(duì)象上執(zhí)行操作。派生類(lèi)構(gòu)造器將在基類(lèi)的所有委托構(gòu)造器執(zhí)行完畢后執(zhí)行。
關(guān)于基類(lèi)構(gòu)造器,C++0x將允許一個(gè)類(lèi)指定需要繼承的基類(lèi)構(gòu)造器。這意味著C++0x編譯器將產(chǎn)生代碼用于類(lèi)繼承,即將子類(lèi)構(gòu)造轉(zhuǎn)發(fā)為基類(lèi)構(gòu)造。注意這是一個(gè)要么全部要么沒(méi)有的特性:或者全部構(gòu)造器被轉(zhuǎn)發(fā),或者沒(méi)有構(gòu)造器被轉(zhuǎn)發(fā)。另外注意在多重繼承的情況下有些限制,比如類(lèi)構(gòu)造器不能繼承來(lái)自兩個(gè)類(lèi)的具有相同簽名的構(gòu)造器。同時(shí)子類(lèi)構(gòu)造器的簽名也不能與用來(lái)繼承的基類(lèi)構(gòu)造器相匹配。
相應(yīng)語(yǔ)法如下:
class BaseClass
{
public:
BaseClass(int iValue);
};
class DerivedClass:public BaseClass
{
public:
using default BaseClass;
};
關(guān)于成員初始化,C++0x將允許以下語(yǔ)法:
class SomeClass
{
public:
SomeClass() {}
explicit SomeClass(int iNewValue):iValue(iNewValue) {}
private:
int iValue = 5;
};
該類(lèi)的任何構(gòu)造器都將把iValue初始化為5,除非它提供自己的實(shí)現(xiàn)來(lái)改變這種行
為。所以上面的空構(gòu)造器將按照類(lèi)的定義來(lái)初始化iValue,而接受int的構(gòu)造器則
會(huì)用給定的參數(shù)來(lái)初始化iValue。
在現(xiàn)行標(biāo)準(zhǔn)中,常量0既是常量整數(shù)又是空指針,充當(dāng)著雙重角色。這一行為自1972年C語(yǔ)言早期以來(lái)便一直存在。
多年來(lái),程序員為了避免這種語(yǔ)義模糊多采用標(biāo)識(shí)符NULL來(lái)代替0。然而,兩項(xiàng)C++語(yǔ)言的設(shè)計(jì)選擇集中產(chǎn)生了另一項(xiàng)語(yǔ)義模糊。在C語(yǔ)言中,NULL作為預(yù)編譯宏被定義為((void*)0)或0。在C++語(yǔ)言中,void*不能隱式轉(zhuǎn)換為其他指針類(lèi)型,因而在前項(xiàng)定義下,簡(jiǎn)單如char* c = NULL的代碼會(huì)通不過(guò)編譯。為解決此問(wèn)題,C++確保NULL展開(kāi)為0,并作為一種特例允許其轉(zhuǎn)換為其他指針類(lèi)型。這一選擇在同重載機(jī)制交互時(shí)產(chǎn)生了麻煩。比如,假設(shè)程序中存在以下聲明:
void foo(char *);
void foo(int);
現(xiàn)在調(diào)用foo(NULL),則foo(int)版本將會(huì)被調(diào)用,幾乎可以肯定這不是程序員的意圖。
新標(biāo)準(zhǔn)很可能引入一個(gè)專(zhuān)用于空指針的關(guān)鍵字,目前nullptr承擔(dān)這一角色。
nullptr不能被賦給整型,也不能與整型相比較,但它可以向其他指針類(lèi)型賦值并與之比較。
0的現(xiàn)存角色將會(huì)因顯而易見(jiàn)的兼容性理由而得到保留。
如果新的語(yǔ)法取得成功,C++委員會(huì)可能會(huì)將把0和NULL當(dāng)作空指針的做法標(biāo)記為已廢棄特性,并最終廢棄這種雙重角色。
在標(biāo)準(zhǔn)C++語(yǔ)言中,枚舉不是類(lèi)型安全的。枚舉值實(shí)際上就是整數(shù),即便其枚舉類(lèi)型各不相同。這樣不同枚舉類(lèi)型的兩個(gè)枚舉值相互比較就成為可能。這方面C++03所提供的唯一安全特性為:一個(gè)整數(shù)或一種枚舉類(lèi)型的值不能隱式轉(zhuǎn)換為其他枚舉類(lèi)型的值。另外,枚舉所使用的整形的大小無(wú)法由用戶指定,而只能由實(shí)現(xiàn)定義。最后,枚舉值的作用域?yàn)槊杜e的外圍作用域。這就意味著兩個(gè)不同的枚舉不能包含名字相同的成員。
C++0x將允許創(chuàng)建一類(lèi)沒(méi)有上述問(wèn)題的特殊枚舉。這種枚舉通過(guò)enum class來(lái)聲明:
enum class Enumeration
{
Val1,
Val2,
Val3 = 100,
Val4 /* = 101 */,
};
這種枚舉是類(lèi)型安全的。枚舉類(lèi)的值不會(huì)被隱式轉(zhuǎn)換為整數(shù),這樣它們也不會(huì)被拿來(lái)與整數(shù)相比較。(Enumeration::Val4 == 101會(huì)產(chǎn)生編譯錯(cuò)誤)
枚舉類(lèi)所使用的類(lèi)型可以明確指定。缺省情況下,如同上例,為int。但這也能像下例那樣改變:
enum class Enum2 : unsigned int {Val1, Val2};
這種枚舉的作用域也被指定為枚舉類(lèi)名的作用域。使用這種枚舉名必須顯式指定作用域。Val1無(wú)定義,而Enum2::Val1有定義。
另外,C++0x也將允許標(biāo)準(zhǔn)枚舉提供其作用域范圍以及其使用的整型大小。
enum Enum3 : unsigned long {Val1 = 1, Val2};
這種枚舉名被定義具有枚舉類(lèi)型作用域,如(Enum3::Val1)。但是,為了保持向后兼容,枚舉名也被放入外圍作用域。
枚舉的前置聲明在C++0x中也將成為可能。以前,枚舉類(lèi)型不能前置聲明的理由是枚舉的大小取決于其內(nèi)容。只要枚舉值的大小在程序中得以指定,枚舉就能前置聲明。
enum Enum1; //在C++和C++0x均合法,未明確標(biāo)明枚舉數(shù)大小
enum Enum2 : unsigned int; //僅C++0x合法
enum class Enum3; //在C++0x中合法,枚舉數(shù)被隱式確定為int型
enum class Enum4: unsigned int; //在C++0x中合法
enum Enum2 : unsigned short; //不合法,Enum2已被定義為不同類(lèi)型
標(biāo)準(zhǔn)C++的詞法分析器在任何場(chǎng)合下都將">>"解釋為右移操作符。然而,在模板定義中,如此解釋兩個(gè)右尖括號(hào)幾乎總是錯(cuò)誤的。
C++0x將改變?cè)~法分析器的規(guī)范以便在合理的情況下能把多個(gè)右尖括號(hào)解釋為模板參數(shù)列表的結(jié)束符??梢杂眯±ㄌ?hào)來(lái)改變這種行為。
template<bool bTest> SomeType;
std::vector<SomeType<1>2>> x1;// 解讀為 std::vector of "SomeType<true> 2>",非法。整數(shù) 1 被隱形轉(zhuǎn)換為 bool 類(lèi)型值 true
std::vector<SomeType<(1>2)>> x1;// 解讀為 std::vector of "SomeType<false>", 合法的 C++0x 表示式, (1>2) 被轉(zhuǎn)換為 bool 類(lèi)型值 false
標(biāo)準(zhǔn)C++為構(gòu)造器添加了explicit關(guān)鍵字作為修飾符以防止只有單個(gè)參數(shù)的構(gòu)造器使用隱式轉(zhuǎn)換操作。然而,這種做法對(duì)于真正的轉(zhuǎn)換操作符是無(wú)效的。比如,一個(gè)智能指針類(lèi)可能有一個(gè)bool()操作符以使自身的行為更像原生指針。如果它包含這種轉(zhuǎn)換操作,它就能用if(smart_ptr_variable)來(lái)測(cè)試。(若指針?lè)强談t測(cè)試是為真,否則測(cè)試為假。)然而,這種操作也會(huì)導(dǎo)致其他出乎意料的轉(zhuǎn)換。由于C++的bool被定義為一種算術(shù)類(lèi)型,這種指針也就可以被隱式轉(zhuǎn)換為整型甚而浮點(diǎn)類(lèi)型,從而導(dǎo)致一些用戶并不希望出現(xiàn)的算術(shù)操作。
在C++0x中,explicit關(guān)鍵字將能用于轉(zhuǎn)換操作符的定義中。與構(gòu)造器一樣,它將防止進(jìn)一步的隱式轉(zhuǎn)換。
在進(jìn)入這個(gè)主題之前,各位應(yīng)該先弄清楚“模板”和“類(lèi)型”本質(zhì)上的不同。class template (類(lèi)模板,是模板)是用來(lái)產(chǎn)生 template class (模板類(lèi),是類(lèi)型)。
在標(biāo)準(zhǔn) C++,typedef 可定義模板類(lèi)一個(gè)新的類(lèi)型名稱(chēng),但是不能夠使用 typedef 來(lái)定義模板的別名。舉例來(lái)說(shuō):
template<typename first, typename second, int third>
class SomeType;
template<typename second>
typedef SomeType<OtherType, second, 5> TypedefName; // 在C++是不合法的
不能通過(guò)編譯。
為了定義模板的別名,C++0x 將會(huì)增加以下的語(yǔ)法:
template<typename first, typename second, int third>
class SomeType;
template<typename second>
using TypedefName = SomeType<OtherType, second, 5>;
using 也能在 C++0x 中定義一般類(lèi)型的別名,等同 typedef:
typedef void(*PFD)(double);// 傳統(tǒng)語(yǔ)法
using PF = void (*)(double); // 新增語(yǔ)法
在標(biāo)準(zhǔn) C++ 中,并非任意的類(lèi)型都能做為 union 的成員。比方說(shuō),帶有非缺省構(gòu)造函數(shù)的類(lèi)型就不能是 union 的成員。在新的標(biāo)準(zhǔn)里,移除了所有對(duì) union 的使用限制,除了其成員仍然不能是引用類(lèi)型。 這一改變使得 union 更強(qiáng)大,更有用,也易于使用。
以下為 C++0x 中 union 使用的簡(jiǎn)單樣例:
struct point
{
point() {}
point(int x, int y): x_(x), y_(y) {}
int x_, y_;
};
union
{
int z;
double w;
point p; // 不合法的 C++; point 有一 non-trivial 建構(gòu)式
// 合法的 C++0x
};
這一改變僅放寬 union 的使用限制,不會(huì)影響既有的舊代碼。
在 C++0x 之前, 不論是模板類(lèi)或是模板函數(shù),都只能按其被聲明時(shí)所指定的樣子,接受一組固定數(shù)目的模板實(shí)參; C++0x 加入新的表示法,允許任意個(gè)數(shù)、任意類(lèi)別的模板實(shí)參,不必在定義時(shí)將實(shí)參的個(gè)數(shù)固定。
template<typename... Values>class tuple;
模板類(lèi) tuple 的對(duì)象,能接受不限個(gè)數(shù)的 typename 作為它的模板形參:
class tuple<int, std::vector<int>, std::map<std::string, std::vector<int>>> someInstanceName;
實(shí)參的個(gè)數(shù)也可以是 0,所以 class tuple<> someInstanceName 這樣的定義也是可以的。
若不希望產(chǎn)生實(shí)參個(gè)數(shù)為 0 的變長(zhǎng)參數(shù)模板,則可以采用以下的定義:
template<typename First, typename... Rest>class tuple;
變長(zhǎng)參數(shù)模板也能運(yùn)用到模板函數(shù)上。 傳統(tǒng) C 中的 printf 函數(shù)雖然也能達(dá)成不定個(gè)數(shù)的形參的調(diào)用,但其并非類(lèi)型安全。 以下的樣例中,C++0x 除了能定義類(lèi)別安全的變長(zhǎng)參數(shù)函數(shù)外,還能讓類(lèi)似 printf 的函數(shù)能自然地處理非自帶類(lèi)別的對(duì)象。 除了在模板實(shí)參中能使用...表示不定長(zhǎng)模板實(shí)參外,函數(shù)實(shí)參也使用同樣的表示法代表不定長(zhǎng)實(shí)參。
template<typename... Params>
void printf(const std::string& strFormat, Params... parameters);
其中,Params 與 parameters 分別代表模板與函數(shù)的變長(zhǎng)參數(shù)集合, 稱(chēng)之為實(shí)參包 (parameter pack)。實(shí)參包必須要和運(yùn)算符“...”搭配使用,避免語(yǔ)法上的歧義。
變長(zhǎng)參數(shù)模板中,變長(zhǎng)參數(shù)包無(wú)法如同一般實(shí)參在類(lèi)或函數(shù)中使用; 因此典型的手法是以遞歸的方法取出可用實(shí)參。
標(biāo)準(zhǔn)C++提供兩種字符串常量。第一種,包含在雙引號(hào)之間,生成一個(gè)以'\0'結(jié)尾的const char類(lèi)型的數(shù)組。第二種,形為L(zhǎng)"",生成一個(gè)以L'\0'結(jié)尾的const wchar_t類(lèi)型的數(shù)組。這里wchar_t為寬字符。這兩種字符串常量均沒(méi)有對(duì)Unicode編碼的字符串提供支持。
為了改進(jìn)C++編譯器對(duì)Unicode的支持,char類(lèi)型的定義被修改成除了包含編譯器基本執(zhí)行字符集的所有成員之外還將至少包括UTF-8中的8位編碼。以前它只包含前者。
C++0x將包括三種Unicode編碼的字符串分別用于支持UTF-8,UTF-16,及 UTF-32。
標(biāo)準(zhǔn)C++提供了數(shù)種字面值。字符"12.5"是能夠被編譯器解釋為數(shù)值12.5的double類(lèi)別字面值。然而,加上"f"的后置,像是"12.5f",則會(huì)產(chǎn)生數(shù)值為12.5的float類(lèi)別字面值。在C++規(guī)范中字面值的后置是固定的,而且C++代碼并不允許創(chuàng)立新的字面后置。
C++1x (?)開(kāi)放用戶定義新的字面修飾符(literal modifier),利用自定義的修飾符完成由字面值建構(gòu)對(duì)象。
字面值轉(zhuǎn)換可以區(qū)分為兩個(gè)階段:轉(zhuǎn)換前與轉(zhuǎn)換后 (raw 與 cooked)。 轉(zhuǎn)換前的字面值指特定字符串行,而轉(zhuǎn)換后的字面值則代表另一種類(lèi)別。 如字面值1234,轉(zhuǎn)換前的字面值代表 '1', '2', '3', '4' 的字符串行; 而轉(zhuǎn)換后,字面值代表整數(shù)值1234。 另外,字面值0xA轉(zhuǎn)換前是串行'0', 'x', 'A';轉(zhuǎn)換后代表整數(shù)值 10。
C++標(biāo)準(zhǔn)委員會(huì)計(jì)劃為多線程提供標(biāo)準(zhǔn)化支持。
有兩部分將被涉及:對(duì)允許多線程在同一個(gè)程序中共存的內(nèi)存模型提供定義,對(duì)這些線程間的交互提供支持。后者將通過(guò)類(lèi)設(shè)施來(lái)實(shí)現(xiàn)。
內(nèi)存模型對(duì)于描述多個(gè)線程在何種情況下可以訪問(wèn)相同的內(nèi)存地址是不可或缺的。
一個(gè)遵守相應(yīng)規(guī)則的程序可以保證運(yùn)行正常,而一個(gè)破壞相應(yīng)規(guī)則的程序則可能會(huì)因?yàn)榫幾g器的優(yōu)化行為以及內(nèi)存一致性等問(wèn)題而出現(xiàn)不可預(yù)料的行為。
在多線程環(huán)境下,每個(gè)線程通常都擁有一些自己所獨(dú)有的變量。對(duì)于函數(shù)的局部變量來(lái)說(shuō)確實(shí)如此,而對(duì)于全局以及靜態(tài)變量來(lái)說(shuō)就不是這樣。
除了現(xiàn)存的靜態(tài),動(dòng)態(tài)以及自動(dòng)存儲(chǔ)方式以外,在下一個(gè)標(biāo)準(zhǔn)中還將提議增加一種局部于線程的存儲(chǔ)方式。線程局部存儲(chǔ)方式將通過(guò)thread_local存儲(chǔ)指示符來(lái)指定。
一個(gè)本來(lái)可能具有靜態(tài)存儲(chǔ)方式的對(duì)象(即生命期跨越程序整個(gè)執(zhí)行過(guò)程的對(duì)象)現(xiàn)在可能會(huì)被賦予線程存儲(chǔ)方式。與靜態(tài)存儲(chǔ)的對(duì)象相似,線程局部的對(duì)象應(yīng)該也能用構(gòu)造器構(gòu)建并用析構(gòu)器銷(xiāo)毀。
顯式使用/不使用C++類(lèi)的某些(缺?。┏蓡T函數(shù)在標(biāo)準(zhǔn)C++語(yǔ)言中,如果對(duì)象自己不提供,編譯器會(huì)自動(dòng)產(chǎn)生一個(gè)缺省構(gòu)造器,一個(gè)拷貝構(gòu)造器,一個(gè)拷貝賦值運(yùn)算符operator=,以及一個(gè)析構(gòu)器。如前所述,用戶可以通過(guò)提供自己的版本來(lái)覆蓋這些缺省實(shí)現(xiàn)。C++還定義了一些能作用于所有類(lèi)的全局操作符(如operator=和operator new),當(dāng)然用戶也能覆蓋其實(shí)現(xiàn)。
問(wèn)題在于用戶對(duì)于這些默認(rèn)產(chǎn)生的函數(shù)缺乏控制。例如,如果需要使某個(gè)類(lèi)不可拷貝,用戶需要聲明一個(gè)私有的拷貝構(gòu)造器,一個(gè)拷貝賦值運(yùn)算符,同時(shí)略去這些函數(shù)的定義。試圖使用這些函數(shù)將產(chǎn)生編譯錯(cuò)誤或鏈接錯(cuò)誤。然而這不是一個(gè)理想的解決方案。
而且,對(duì)于缺省構(gòu)造器而言,明確告訴編譯器產(chǎn)生這類(lèi)函數(shù)通常非常有用。如果對(duì)象已經(jīng)具有了任何構(gòu)造器,編譯器就不會(huì)為它提供缺省構(gòu)造器。這在許多場(chǎng)合下的確有用,但有時(shí)也需要同時(shí)擁有一個(gè)編譯器產(chǎn)生的缺省構(gòu)造器以及另一個(gè)專(zhuān)用構(gòu)造器。
C++0x將允許用戶明確指定使用或不使用這些標(biāo)準(zhǔn)的對(duì)象函數(shù)。比如,下面這個(gè)類(lèi)型明確聲明它會(huì)使用缺省構(gòu)造器。
struct SomeType
{
//使用缺省構(gòu)造函數(shù)
SomeType() = default;
SomeType(OtherType value);
};
另外,一些特性也能得到明確禁止。比如,下面這個(gè)類(lèi)型是不可拷貝的。
struct NonCopyable
{
NonCopyable & operator=(const NonCopyable &) = delete;
NonCopyable(const NonCopyable &) = delete;
NonCopyable() = default;
};
一個(gè)類(lèi)型可以明確禁止用operator new分配:
struct NonNewable
{
void *operator new(std::size_t) = delete;
};
這個(gè)對(duì)象只能在棧里面分配或成為其他類(lèi)型的成員。不使用不可移植的手段將無(wú)法在堆中分配該對(duì)象。由于使用就地分配new操作符是調(diào)用構(gòu)造器在用戶所分配的內(nèi)存中創(chuàng)建對(duì)象的唯一途徑,并且這一途徑已經(jīng)被上述代碼所禁止,可以肯定對(duì)象將無(wú)法被適當(dāng)創(chuàng)建。
指示符= delete可被用于禁止調(diào)用任意函數(shù),當(dāng)然它也能用于禁止調(diào)用具有某些特定參數(shù)的成員函數(shù)。比如:
struct NoDouble
{
void f(int i);
void f(double) = delete;
};
編譯器將會(huì)拒絕用double值調(diào)用f()的企圖,而不會(huì)無(wú)聲無(wú)息地將其轉(zhuǎn)換為int。這種方法可以被一般化從而禁止使用int以外的任何類(lèi)型調(diào)用該函數(shù),代碼如下:
struct OnlyInt
{
void f(int i);
template<class T> void f(T) = delete;
};
在32位系統(tǒng)中,存在一種至少64位的long long整數(shù)類(lèi)型十分有用。C99標(biāo)準(zhǔn)將此類(lèi)型引入標(biāo)準(zhǔn)C,大多數(shù)C++編譯器也早就將它作為一種擴(kuò)展。事實(shí)上,一些編譯器在C99引入它很久之前就已經(jīng)提供支持。C++0x將把這種類(lèi)型加入標(biāo)準(zhǔn)C++。
C++提供兩種方式測(cè)試斷言,宏assert以及預(yù)編譯命令#error,但是這兩者對(duì)于模版來(lái)說(shuō)都不合用。宏在運(yùn)行期測(cè)試斷言,而預(yù)編譯命令則在預(yù)編譯期測(cè)試斷言,這時(shí)候模版還未能實(shí)例化。所以它們都不適合來(lái)測(cè)試牽扯到模板實(shí)參的相關(guān)特性。
新的機(jī)能會(huì)引進(jìn)新的方式可以在編譯期測(cè)試assertion,只要使用新的關(guān)鍵字static_assert。 聲明采取以下的形式:
static_assert( constant-expression, error-message ) ;
這里有一些如何使用static_assert的例子:
static_assert(3.14< GREEKPI && GREEKPI <3.15, "GREEKPI is inaccurate!");
template<class T >
struct Check {
static_assert(sizeof(int)<=sizeof(T), "T is not big enough!");
};
允許在沒(méi)有提供類(lèi)實(shí)例的前提下作用于類(lèi)的成員,無(wú)需明確的對(duì)象。
在標(biāo)準(zhǔn)C++,sizeof可以作用在對(duì)象以及類(lèi)別上。但是不能夠做以下的事:
struct SomeType { OtherType member;};sizeof(SomeType::member);// 直接由SomeType型別取得非靜態(tài)成員的大小
這會(huì)傳回OtherType的大小。C++03并不允許這樣做,所以會(huì)引發(fā)編譯錯(cuò)誤。C++0x將會(huì)允許這種使用。
C++0x不會(huì)直接提供透明垃圾收集機(jī)制。作為替代,C++0x標(biāo)準(zhǔn)將包含一些有利于在C++實(shí)現(xiàn)垃圾收集機(jī)制的特性。
對(duì)垃圾收集機(jī)制的完全支持將會(huì)延遲到標(biāo)準(zhǔn)的下一個(gè)版本,或者是一個(gè)技術(shù)報(bào)告。
聯(lián)系客服