logo资料库

C++11标准发布文档.pdf

第1页 / 共31页
第2页 / 共31页
第3页 / 共31页
第4页 / 共31页
第5页 / 共31页
第6页 / 共31页
第7页 / 共31页
第8页 / 共31页
资料共31页,剩余部分请下载后查看
C++11 维基百科,自由的百科全书 C++11 , 先 前 被 稱 作 C++0x, 即 ISO/IEC 14882:2011 , 是 C++ 程 式 語 言 的 一 个 標 準 。 它 取 代 第 二 版 標 準 ISO/IEC 14882:2003(第一版ISO/IEC 14882:1998公開於1998年,第二版於2003年更新,分别通稱C++98以及C++03,两者差异很 小),且已被C++14取代。相比于C++03,C++11標準包含核心語言的新機能,而且擴展C++標準程式庫,併入了大部分的 C++ Technical Report 1程式庫(數學的特殊函式除外)。 ISO/IEC JTC1/SC22/WG21 C++標準委員會計劃在2010年8月之前 完成對最終委員會草案的投票,以及於2011年3月召開的標準會議完成國際標準的最終草案。然而,WG21預期ISO將要花 費六個月到一年的時間才能正式發佈新的C++標準。為了能夠如期完成,委員會決定致力於直至2006年為止的提案,忽略 新的提案[1]。最终于2011年8月12日公布,并于2011年9月出版。 2012年2月28日的國際標準草案[1]是最接近于C++11标准的草案,差异仅有编辑上的修正。 像C++這樣的程式語言,透過一種演化的的過程來發展其定義。這個過程不可避免地將引發與現有程式碼的相容問題,在 C++的發展過程中偶爾會發生。不過根據比雅尼·斯特劳斯特鲁普(C++的創始人並且是委員會的一員)表示,新的標準將 幾乎100%相容於現有標準。
目录 设计原则 C++核心語言的擴充 核心語言的執行期表現強化 右值引用和move語意 constexpr – 泛化的常數表示式 對POD定義的修正 核心語言建構期表現的加強 外部模板 核心語言使用性的加強 初始化列表 統一的初始化 型別推導 基于范围的for迴圈 Lambda函式與表示式 返回型別後置的函式宣告 物件建構的改良 顯式虛函數重載 空指標 強型別列舉 角括號 顯式型別轉換子 模板的別名 無限制的unions 核心語言能力的提升 可变参数模板 新的字串字面值 用户定义字面量 多工記憶體模型 thread-local的存儲期限 使用或禁用物件的預設函式 long long int型別 靜態assertion 允許sizeof運算子作用在類別的資料成員上,無須明確的物件 垃圾回收機制 C++標準程式庫的變更 標準庫元件上的升級 執行緒支援 多元組型別 雜湊表 正規表示式 通用智能指針 可扩展的随机数功能 包装引用
多态函数对象包装器 用於元編程的型別屬性 用于计算函数对象返回类型的统一方法 iota 函数 已被移除或是不包含在C++11標準的特色 被移除或廢棄的特色 编译器实现 關聯項目 參考資料 C++標準委員會文件 文章 外部連結 设计原则 C++的修訂包含核心語言以及標準程式庫。 在發展新標準的每個機能上,委員會採取了幾個方向: 維持穩定性和與C++98,可能的話還有C之間的兼容性; 儘可能不透過核心語言的擴展,而是透過標準程式庫來引進新的特性; 能夠演進編程技術的變更優先; 改進C++以幫助系統以及函式庫設計,而不是引進只針對特別應用的新特性; 增進型別安全,提供對現行不安全的技術更安全的替代方案; 增進直接對硬體工作的能力與表現; 提供現實世界中問題的適當解決方案; 實行“zero-overhead”原則(某些功能要求的額外支援只有在該功能被使用時才能使用); 使C++易於教授與學習 关照初學者被認為是重要的,因為他們構成了計算機程序員的主體。也因為許多初學者不願擴展他們對C++的知識,只限 於使用他們對C++專精的部分。此外,考慮到C++被廣泛的使用(包含應用領域和編程風格),即便是最有經驗的程序員在 面對新的編程范式時也會成為初學者。 C++核心語言的擴充 C++委員會的主要作用之一是改善語言核心。核心語言將被大幅改善的領域包括多线程支援、泛型編程、統一的初始化, 以及效能表現的加強。 在此分成4個區塊來討論核心語言的特色以及變更: 執行期表現強化、建構期表現強化、可用性強化,還有新的功能。某 些特性可能會同時屬於多個區塊,但在此僅於其最具代表性的區塊描述。 核心語言的執行期表現強化 以下的語言機能主要用來提升某些效能表現,像是記憶體或是速度上的表現。 右值引用和move語意
在C++03及之前的標準,臨時對象(稱為右值"R-values",因为它们通常位於賦值運算子右侧)無法被改變,在C中亦同 (且被視為等同于const T&)。儘管如此,在某些情況下臨時對象仍然可能會被改變,但这种表现也被視為是一個有用的 漏洞。 C++11增加一個新的非常數引用(reference)型別,稱作右值引用(R-value reference),標記為T &&。右值引用所绑定的 臨時對象可以在該臨時對象被初始化之後做修改,這是為了允許move語意。 C++03低性能问题的之一,就是在以传值方式传递对象时隐式发生的耗時且不必要的深度拷貝。舉例而言, std::vector本质上是一个C-style陣列及其大小的封裝,如果一個std::vector的臨時物件是在函式内部或者 函数返回时创建,要將其儲存就只能透過生成新的std::vector並且把該臨時物件所有的資料複製过去(為了討論上 的方便,這裡忽略返回值优化)。然后該臨時物件會被析构,其使用的記憶體会被释放。 在C++11,把一個vector的右值引用作为参数std::vector的"move建構子",可以把右值参数所绑定的vector內部的指 向C-style陣列的指標複製给新的vector,然後把该指標置null。由于临时变量不会被再次使用,所以不会有代码去访问该 null指针;又因为该指针为null,当该临时对象超出作用域时曾经指向的內部C-style陣列所使用的内存不会被释放。因此, 该操作不仅无形中免去了深拷贝的开销,而且还很安全。 右值引用作为数据类型的引入,使得函数可以重载区分它的参数是值类型、传统的左值引用还是右值引用。这让除了标准 库的现有代码无须任何改动就能等到性能提升。一个返回std::vector的函数的返回类型无须为了调用move构造函 数而显式修改为std::vector&&,因为临时对象自动作为右值。(但是,如果std::vector是没有move构造函 数的C++03版,由于传统的左值引用也可以绑定到临时对象上,因此具有const std::vector&参数的复制构造函数 会被调用,导致一次显著的内存分配。) 出于安全的考虑,推行了一些限制。具名的变量被認定為左值,即使它是被宣告为右值引用数据类型;為了獲得右值必須 使用显式类型转换,如模板函数std::move()。右值引用所绑定的对象应该只在特定情境下被修改,主要用于move 构造函数中。 bool is_r_value (int &&) { return true; } bool is_r_value (const int &) { return false; } void test(int && i) { is_r_value (i); // i為具名變數,即使被宣告成右值引用类型,i作为实参表达式也不會被認定是右值表达式。 is_r_value (std::move(i)); // 使用std::move() 取得右值。 } 由於右值引用的语义特性以及對於左值引用(L-value references;regular references)的某些语义修正,右值引用让開發者能 够提供函数参数的完美轉發(perfect function forwarding)。當與不定長參數模板結合,這項能力允許函式模板能夠完美地 轉送参数給其他接受這些特定参数的函式。最大的用處是轉送建構子參數,創造出能夠自動為這些特定参数呼叫正確建構 式的工廠函式(factory function)。这个用法可以在C++标准库中的emplace_back方法中看到。 constexpr – 泛化的常數表示式 C++本來就已具備常數表示式(constant expression)的概念。像是3+4總是會產生相同的結果並且沒有任何的副作用。常數 表示式對編譯器來說是最佳化的機會,編譯器時常在編譯期執行它們並且將值存入程式中。同樣地,在許多場合下, C++規格要求使用常數表示式。例如在陣列大小的定義上,以及列舉值(enumerator values)都要求必須是常數表示式。 然而,常數表示式不能含有函式呼叫或是物件建構式。所以像是以下的例子是不合法的: int GetFive () {return 5;} int some_value [GetFive () + 7];// 欲產生12個整數的陣列。不合法的C++寫法 這在C++03中是不合法的,因為GetFive() + 7並不是常數表示式。C++03編譯器無從得知GetFive實際上在執行期是常 數。理論上而言,這個函式可能會影響全域變數,或者呼叫其他的非執行期(non-runtime)常數函式等。
C++11引進關鍵字constexpr允許使用者保證函式或是物件建構式是編譯期常數。以上的例子可以被寫成像是下面這樣: constexpr int GetFive () {return 5;} int some_value [GetFive () + 7];// 欲產生12個整數的陣列。合法的C++11寫法 這使得編譯器能夠瞭解並去驗證GetFive是個編譯期常數。 用constexpr修饰函数将限制函式的行为。首先,該函式的回返值型別不能為void。第二,函式的內容必須依照"return expr"的形式。第三,在参数替换後,expr必須是個常數表示式。這些常數表示式只能夠呼叫其他被定義為constexpr的 函式,或是其他常數表示式的資料變數。最後,有著這樣修饰符的函式直到在該編譯單元內被定義之前是不能夠被呼叫 的。 声明为constexpr的函数也可以像其他函数一样用于常量表达式以外的地方,此时不需要满足后两点。 C++11之前,可以在常量表达式中使用的的变量必须被声明为const,用常量表达式来初始化,并且必须是整型或枚举类 型。C++11去除了变量必须是整型或枚举类型的限制,只要变量使用了constexpr关键字来定义: constexpr double earth_gravitational_acceleration = 9.8; constexpr double moon_gravitational_acceleration = earth_gravitational_acceleration / 6.0; 这些变量都是隐式常量,必须使用常量表达式来初始化。 為了让使用者自定义型別(user-defined type)参与建構常量表示式,建構式也可以用constexpr来声明。與constexpr 函式一樣,constexpr建構式必須在該編譯單元內使用之前被定義。它的函数体必须为空。它必須用常量表示式初始化他 的成員(member)。而這種型別的解構式應當是平凡的(trivial)。 拥有constexpr构造函数的类型的复制构造函数通常也應該被定義為constexpr,以便该类型的对象以值传递的方式从 constexpr函数返回。该類別的任何成員函式,像是複製建構式、运算符重载函数等等,只要他們符合常數表达式函式的 定義,都可以被宣告成constexpr。使得編譯器能夠在編譯期進行類別的複製、對他們施行運算等等。 常數表达式函式或建構式,可以以非常數表示式(non-constexpr)作为參數喚起。就如同constexpr整數字面值能夠 指派給non-constexpr變數,constexpr函式也可以接受non-constexpr參數,其結果儲存於non-constexpr變 數。constexpr關鍵字只有當表示式的成員都是constexpr,才允許編譯期常數性的可能。 對POD定義的修正 在C++03中,一個类(class)或結構(struct)要想被作为POD,必須遵守幾條規則。符合這種定義的型別能夠產生與C相 容的物件内存佈局(object layout),而且可以被静态初始化。但C++03标准严格限制了何种类型与C兼容或可以被静态初 始化的,尽管并不存在技术原因导致编译器无法处理。如果创建一个C++03 POD类型,然后为其添加一个非虚成员函数, 这个类型就不再是POD类型了,从而无法被静态初始化,也不再与C兼容,尽管其内存布局并没有发生变化。 C++11通过把POD概念划分成两个概念:平凡的(trivial)和标准布局(standard-layout),放寬了關於POD的定義。 一个平凡的类型可以被静态初始化,同时意味着使用memcpy来复制数据是合法的,而无须使用复制构造函数。平凡的类型 对象的生命周期开始于其存储空间被分配时,而不是其构造函数完成时。 一個平凡的的類別或結構符合以下定義: 1. 平凡的預設建構式。這可以使用預設建構式語法,例如SomeConstructor() = default; 2. 平凡的複製建構式和move构造函数,可使用預設語法(default syntax) 3. 平凡的賦值運算子和move赋值操作符,可使用預設語法(default syntax) 4. 平凡的解構式,不可以是虛函数(virtual)
只有在类没有虚基类和虚成员函数时,构造函数才是平凡的。复制构造函数和赋值操作符还额外要求所有非静态数据成员 都是平凡的。 一个符合标准布局的类封装成员的方式与C兼容。一個標準佈局(standard-layout)的類別或結構符合以下定義: 1. 只有非靜態的(non-static)資料成員,且這些成員也是符合標準佈局的型別 2. 對所有non-static成員有相同的存取控制(public,private,protected) 3. 沒有虛擬函式 4. 沒有虛擬基礎類別 5. 只有符合標準佈局的基礎類別 6. 沒有和第一個定義的non-static成員相同型別的基礎類別 7. 若非沒有帶有non-static成員的基礎類別,就是最底層(繼承最末位)的類別沒有non-static資料成員而且至多一個帶有 non-static成員的基礎類別。基本上,在該類別的繼承體系中只會有一個類別帶有non-static成員。 一个类、结构、联合只有在其是平凡的、符合标准布局,并且所有非静态成员和基类都是POD时,才被视为POD。 通过划分,使得放弃一个特性而不失去另一个成为可能。一个具有复杂的复制和move构造函数的类可能不是平凡的,但是 它可能符合标准布局,从而能与C程序交互。类似地,一个同时具有public和private数据成员的类不符合标准布局,但它可 以是平凡的,从而能够使用memcpy来复制。 核心語言建構期表現的加強 外部模板 在標準C++中,只要在編譯單元內遇到被完整定義的模板,编译器都必須將其实例化(instantiate)。這會大大增加編譯時 間,特別是模板在許多編譯單元內使用相同的參數具現化。看起来沒有辦法告訴C++不要引發模板的具現化。 C++11將會引入外部模板这一概念。C++已經有了強制編譯器在特定位置开始具現化的語法: template class std::vector ; 而C++所缺乏的是阻止編譯器在某個編譯單元內具現化模板的能力。C++11將簡單地擴充前文語法如下: extern template class std::vector ; 這样就告訴編譯器不要在該編譯單元內將該模板具現化。 核心語言使用性的加強 這些特色存在的主要目的是為了使C++能夠更容易使用。舉凡可以增進型別安全,減少程式碼重複,不易誤用程式碼之類 的。 初始化列表 標準C++從C帶來了初始化列表(initializer list)的概念。這個構想是結構或是数组能夠依據成員在該結構內定義的順序透 過給予的一串引數來產生。這些初始化列表是遞迴的,所以結構的数组或是包含其他結構的結構可以使用它們。這對靜態 列表或是僅是把結構初始化為某值而言相當有用。C++有构造函数,能夠重複对象的初始化。但單單只有那樣並不足以取 代這項特色的所有機能。在C++03中,只允許在嚴格遵守POD的定義和限制條件的結構及類別上使用這項機能,非POD的 型別不能使用,就連相當有用的STL容器std::vector也不行。 C++11將會把初始化列表的概念綁到型別上,稱作std::initializer_list。這允許构造函数或其他函数像參數般地使 用初始化列表。舉例來說:
class SequenceClass { public : SequenceClass (std::initializer_list list); }; 這將允許SequenceClass由一連串的整數构造,就像: SequenceClass someVar = {1, 4, 5, 6}; 這個构造函数是種特殊的构造函数,稱作初始化列表构造函数。有著這種构造函数的類別在統一初始化的時候會被特別對 待。 類別std::initializer_list<>是個第一級的C++11標準程式庫型別。然而他們只能夠經由C++11編譯器透過{}語法的 使用被靜態地构造。這個列表一經构造便可複製,雖然這只是copy-by-reference。初始化列表是常數;一旦被建立,其成員 均不能被改變,成員中的資料也不能夠被變動。 因為初始化列表是真實型別,除了類別构造式之外還能夠被用在其他地方。正規的函数能夠使用初始化列表作為形参。例 如: void FunctionName (std::initializer_list list); FunctionName ({1.0f, -3.45f, -0.4f}); 標準容器也能夠以這種方式初始化: vector v = { "xyzzy" , "plugh" , "abracadabra" }; vector v({ "xyzzy" , "plugh" , "abracadabra" }); vector v{ "xyzzy" , "plugh" , "abracadabra" };//见下节“统一的初始化” 統一的初始化 標準C++在初始化型別方面有著許多問題。初始化型別有數種方法,而且交換使用時不會都產生相同結果。傳統的建構式 語法,看起來像是函式宣告,而且為了能使編譯器不會弄錯必須採取一些步驟。只有集合體和POD型別能夠被集合式的初 始化(使用SomeType var = {/*stuff*/};)。 C++11將會提供一種統一的語法初始化任意的物件,它擴充了初始化串列語法: 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的初始化的運作就如同C-style的初始化串列。每個公開的變數將被對應於初始化串列的值給初始化。隱式型別轉換會 在需要的時候被使用,這裡的隱式型別轉換不會產生範圍縮限(narrowing)。要是不能夠轉換,編譯便會失敗。(範圍縮 限 (narrowing):轉換後的型別無法表示原型別。如將32-bit的整數轉換為16-bit或8-bit整數,或是浮點數轉換為整數。) var2的初始化則是簡單地呼叫建構式。
統一的初始化建構能夠免除具體指定特定型別的必要: struct IdString { std::string name; int identifier ; }; IdString var3{"SomeName" , 4}; 該語法將會使用const char *參數初始化std::string。你也可以做像下面的事: IdString GetString () { return {"SomeName" , 4}; // 注意這裡不需要明確的型別 } 統一初始化不會取代建構式語法。仍然會有需要用到建構式語法的時候。如果一個類別擁有初始化列表构造函数 (TypeName(initializer_list);),而初始化串列与构造函数的参数类型一致,那麼它比其他形式的 建構式的優先權都來的高。C++11版本的std::vector將會有初始化串列建構式。這表示: std::vector theVec {4}; 這將會呼叫初始化串列建構式,而不是呼叫std::vector只接受一個尺寸參數產生相應尺寸vector的建構式。要使用這個 建構式,使用者必須直接使用標準的建構式語法。 型別推導 在C++03和C,使用變數必須明確的指出其型別。然而,隨著模版型別的出現以及模板超編程的技巧,某物的型別,特別是 函式定義明確的回返型別,就不容易表示。在這樣的情況下,將中間結果儲存於變數是件困難的事,可能會需要知道特定 的超編程程式庫的內部情況。 C++11提供兩種方法緩解上述所遇到的困難。首先,有被明確初始化的變數可以使用auto關鍵字。這會依據該初始化子 (initializer)的具體型別產生變數: auto someStrangeCallableType = boost::bind(&SomeFunction , _2, _1, someObject ); auto otherVariable = 5; someStrangeCallableType的型別就是模板函式boost::bind對特定引數所回返的型別。作為編譯器語意分析責任的 一部份,這個型別能夠簡單地被編譯器決定,但使用者要透過檢視來判斷型別就不是那麼容易的一件事了。 otherVariable的型別同樣也是定義明確的,但使用者很容易就能判別。它是個int(整數),就和整數字面值的型別一 樣。 除此之外,decltype能夠被用來在編譯期決定一個表示式的型別。舉例: int someInt ; decltype (someInt ) otherIntegerVariable = 5; decltype和auto一起使用會更為有用,因為auto變數的型別只有編譯器知道。然而decltype對於那些大量運用運算子 重載和特化的型別的程式碼的表示也非常有用。 auto對於減少冗贅的程式碼也很有用。舉例而言,程式員不用寫像下面這樣: for (vector ::const_iterator itr = myvec.cbegin (); itr != myvec.cend(); ++itr)
分享到:
收藏