聲明: 所有圖片均為我在騰訊博客的原創, 但是從我的騰訊微博轉過來就被流氓的打了標簽. 因此不涉及版權問題, 可以隨意使用.
C++11 中棄用了 `throw(type1, type2...)` 這種異常聲明方式. 但是庫中大量使用了 noexcept 代替原有異常機制, 因此提高了庫的效率.
默認析構函數是 noexcept(true) 的, 但是如果基類析構函數是 noexcept(false), 那么自雷析構函數默認也是 noexcept(false) 的.
類中成員變量的初始化順序: 1, 就地初始化. 2, 初始化列表. 3, 構造函數體. (目前, 最新的 vs2013 preview 還不持支此特性)
注: 此版本 vs 尚不支持就地初始化, 所以此代碼僅供參考.
委托構造, 即一個構造函數依賴於另一個構造函數.
曾經, C++ 是無法直接獲取類的成員變量的大小的, 通常我們會 `sizeof(reinterpret_cast<X *>(0)->m_foo)`. 而 11 終結了這個蹩腳的語法, 對於普通對象成員使用 `sizeof(X::m_foo)` 即可.
類成員函數后面的 override 和 final 真是一對好朋友. 在編寫多層次繼承時可以極大避免程序員犯錯誤. 我們需要這樣的編譯器! 當然, 如果你非寫成這樣: `virtual foo() final = 0;` 的成員函數, 也是可以的.
關於 `virtual void foo() final = 0;`, 如果面試的時候考官問你: 怎么能讓一個類不能實例化? 除了回答 private dctor 以外, 還可以使用這條技巧.
return rvalue 在表達式結束后, 其生命周期也就結束了, 而通過右值引用的匿名變量, 該右值生命周期將會與右值引用類型的變量的生命周期一樣. 只要這個引用變量還 "活着", 該右值臨時量將會一直 "存活" 下去.
在 98 中, const & 類型也可以將臨時變量的聲明期延長至引用變量周期. 原來如此...
'std::move 基本等同於一個類型轉換: static_cast<T &&>(lvalue);' 這句話驗證了我一年前剛學 move semantic 的疑惑. 我當時就認為, 既然 lvalue 可以通過變為匿名的手段成為 rvalue, 那么 static_cast 就可以勝任. 結果今天終於找到證實了.
std::move 和 std::forward 在實際實現的差別並不大. 不過標准庫這么設計, 也是為了讓每個名字對應不同的用戶, 以應對未來可能的擴展.
標准庫中, std::move_if_noexcept 如果參數有 noexcept 移動構造函數, 則返回右值引用; 否則返回左值引用. 這使得 `T tNew = std::move_if_noexcept(tOld);` 非常安全.
11 將 explicit 關鍵字擴展到類的類型轉換上, 對程序的健壯性起了積極作用. 類似的改動還有類成員函數的 override 和 final.
wiki: "在 C++11 中,關鍵字 explicit 修飾符也能套用到類型轉換子上。如同建構式一樣,它能避免類型轉換子被隱式轉換調用。但 C++11 特別針對布林值轉換提出規范,在 if 條件式,循環,邏輯運算等需要布林值的地方,編譯器能為符合規范的表示式調用用戶自定的布林類型轉換子。" (感謝 rui)
匿名聯合體的成員在 class 中能夠直接被引用. 這是 98 中無法做到的, 通常, 98 會給 union 起一個名字, 然后再通過這個名字引用其中的成員, 着實不直觀而且還麻煩. 起名字是程序員最大的痛苦.
經過測試, 在 vs2013 preview 中, 匿名 struct 和 class 也是被支持的.
用戶自定義字面量為程序員提供了非常直觀 & 方便的語法糖. 我又想起了那句話: 好的編程語言是讓程序員用更少的代碼完成更多的事情. 推薦閱讀: http://akrzemi1.wordpress.com/2013/01/04/preconditions-part-i/ 以及 http://www.cnblogs.com/walfud/articles/3280629.html
字面值為 char: 字面量操作符參數只能為 char.
字面量為 const char *: 操作符參數只接受 (const char *, size_t_).
字面值為浮點型: 字面量操作符只接受 long double 或者 const char * 作參數. 同理, 如果 long double 容納不下, 調用 const char * 版本處理函數.
字面量為整數: 操作符函數只能接受 unsigned long long 或者 const char * 作為參數. 如果 unsigned long long 容納不下, 則自動調用 const char * 操作符處理函數.
11 中, 自定義字面量操作符需要注意: 1, `operator "" _XXX` 中, 雙引號和自定義后綴之間必須有一個空格. 2, 建議使用下划線開始的自定義后綴, 避免和 C/C++ 關鍵字沖突.
inline namespace 我看不出你有什么好. 命名就是破壞了 namespace 的隔離性. 所以 Do NOT use it unless you know what you are doing.
使用 using 定義別名的好處是可以支持 template. 比如 `template <typename T> using MapString = std::map<T, std::string>`. 這是 typedef 無法做到的.
auto 對於 cv-qualifier 的支持: auto 並不從初始化表達式中帶走 cv-qualifier. 但是對於指針和引用則會保持原有的 cv 類型. 見圖片.
在 11 中, 有幾種情況是 auto所不能的:
1: 不能作為函數參數類型, 更不能在函數參數中自動推導.
2: 不能作為結構體非靜態成員, 同樣也不能推導.
3: 不能聲明 auto arr[n] 這樣的數組.
4: 不能作為模板的參數.
decltype 與 auto 不同, decltype 能夠帶走 "cv" 限定符, 不過, decltype 不會繼承對象的 cv 屬性. 如圖:
decltype(v) 也有一些詭異的規則, 且看:
1, 如果 v 是一個不帶括號的標記符表達式(id-expr) 或類成員表達式, 則 decltype(v) 代表 v 的類型. 此外, 如果 v 是一個被重載的函數, 則導致編譯錯誤.
2, 否則, 假設 v 類型是 T, 如果 v 是一個將亡值(xvalue), 那么 decltype(v) 為 T &&.
3, 否則, 假設 v 類型 T, 如果 v 是一個左值, 則 decltype(v) 為 T &.
4, 否則, 假設 v 類型 T, 則 decltype(v) 為 T.
其中最蹩腳的就是規則 3. 看幾個例子: int i = 0; int *pI = &i; decltype(i) => int, decltype(*pI) => int &, decltype(i++) => int &, decltype(++i) => int. <深入理解 C++11> 這書講的不錯, 詳細內容請看 4.3.3
此外, decltype 能夠允許冗余的 cv 限定符. 比如 const int i = 0; const decltype(i) ci = 0; 也是允許的, 雖然 decltype(i) 已經是 const int 類型.
有人認為宏和常量都要全大寫, 這是對全大寫作用的誤讀. 在傳統的 C 語言中, 全大寫是因為宏處理發生在預編譯階段, 任何包含該該字母串的內容都會被替換, 因此會擾亂正常代碼. 而開發者為了避免這種情況, 才將宏全大寫. 枚舉雖然也是常量, 但是會擾亂正常代碼么? 顯然不會, 因此就無需全大寫.
11 終於把 enum 的超級作用域做了完善. 在 98 中, enum 作用域是會被自動導出到父作用域的, 導致不同的枚舉中也不能存在相同的枚舉值, 這是何等違背作用域的理念. 在 11 中, enum class 將枚舉限制在了枚舉類中, 增強了作用域的概念, 因此不同枚舉類中完全可以存在同名同值的枚舉值, 這讓代碼更加一致.
11 中加入了很多對並行編程的支持. 比如 atomic 模板, thread 等, 極大方便了並行程序設計. 讓程序員將精力集中在要解決的問題上, 而不是互斥訪問變量和如何創建線程這些問題上.
在 11 的並行支持中, 有個重要的概念叫做 "內存模型". 目前 11 支持 6 種內存模型. 這些模型分門別類的作為 atomic 對象的 load 和 store 第二參數使用. 其作用就是約束機器指令的執行順序以及添加內存欄柵.
memory_order_relaxed: 不對執行順序作任何保證.
memory_order_acquire: 本線程中, 所有后續的讀操作必須在本原子操作完成后執行.
memory_order_release: 本線程中, 所有之前的寫操作完成之后才能執行本原子操作.
memory_order_acq_rel: 同時包含 memory_order_acquire 和 memory_order_release 標記.
memroy_order_consume: 本線程中, 所有后續的有關本原子類型的操作, 必須在本條原子操作完成之后執行.
memory_order_seq_cst: 全部存取都按順序執行. 這也是 C++11 所有 atomic 原子操作的默認值.
constexpr 可以作為編譯期常量使用, 不過大部分功能還是可以使用傳統 enum 來實現. 但是, 使用 constexpr 進程 metal programming 是一個新趨勢, 如圖: 其中 f 是一個編譯期常量, 通過單步調試可以發現, Fabonacci 無法 step into.
關於可變數量模板, 這里有一份極好的解釋: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2087.pdf. 請搜索 printf 的實現. 11 把更多的工作留給了編譯器去做, 這會不會讓原本已經非常慢的編譯變得更加慢? 我心有余悸, 或是杞人憂天.
變長模板中也存在一些易錯點: 如模板解包的時候, 比如有 `typename ...Arg`, 那么 `class C<Arg...>` 和 `class C<Arg>...` 是不一樣的. 前者代表 `class C<X, Y>`, 后者代表 `class C<X>, class C<Y>`.
空指針一直是 C++ 的一個痛處. 因為 NULL 要么是 0, 那么是 (void *)0. 然而兩者都是用語法技巧來躲避編譯錯誤, 它們的問題如下圖. 對於空指針, 11 給出了更好的答案: nullptr.
nullptr 的類型是 nullptr_t, nullptr_t 的 ==, <=, >= 可以和任何 nullptr_t 以及指針類型做運算, 而且都返回 true. 但是在我的 vs2013preview 里測試, 發現有地址的指針總是大於 nullptr, 因此推斷 vs2013preview 的實現還是一個整數.
此外, 11 規定, nullptr 的大小和 void * 的大小保持一致.
`=delete` 除了可以作用在類中, 還可以作用在類外, 這種用法通常是禁止類型轉換帶來的副作用. 再次聲明, 不要將 `explicit` 與 `=delete` 合用. 因為兩者合用代表 '刪除顯示的構造', 也就是阻止顯示構造而允許隱士構造, 這往往不是我們想要的.
因為 `=delete` 可以作用在普通函數上, 所以我們可以對全局的 new 進行刪除, 這樣就阻止了某個對象在堆上創建. 這真是一個歪點子.
如果你沒使用過 lambda, 那就沒使用過 C++11! lambda 可以方便的將 "短小精悍" 的功能隨意的插入代碼之間. 我們曾經為了和 stl 配合遍歷一個稍復雜些的容器, 通常都寫一個 class 並重載 `operator()`, 這既不直觀, 書寫也不方便. lambda 你姍姍來遲啊...
lambda 的全貌: `[capture](paramters) mutable -> return-value {statement}`. 默認情況下, lambda 總是一個 const 函數, 不過 mutable 可以取消其常量性.
lambda 有一個重要的 "陷阱". 即: 通過 "值傳遞" 方式捕獲的變量, 在其聲明的時候值就已經定好了. 而 "引用傳遞" 方式捕獲的變量, 會隨着程序運行而修改.
只有具有函數作用域的變量, 才能被 lambda 捕獲, 全局變量是不行的. 這也避免了全局變量沒有初始化就被 lambda 使用值傳遞方式捕獲, 而產生錯誤的行為.
有的時候, lambda 會報和構造函數有關的錯誤, 這是因為 lambda 的底層實現可能就是一個匿名重載了 `operator()` 的 class, 也就是我們熟悉的仿函數.
`[](){}` 這就是一個合法的 lambda, 更有甚者, 會寫出這樣的 lambda `[]{}`.
`auto i = [](){return 1;}();` 注意最后的一對小括號, lambda 允許你定義的時候直接調用.
每個 lambda 的作用域都是其所在函數.
lambda 在 11 標准中默認都是內聯的.

另:
關於本書的勘誤, 是我自己總結的, 可能不是很全, 希望能對你有幫助並期待你的補充: http://www.cnblogs.com/walfud/articles/2057799.html (搜索 "<深入理解 C++11: C++11 新特性解析與應用>").
我的一篇關於 C++11 最討人喜歡的幾個特性的博客: http://www.cnblogs.com/walfud/articles/3280629.html
我的另一篇關於 C++11 FAQ 的博客: http://www.cnblogs.com/walfud/articles/2846227.html
某日, 我看了一篇博客, 是說 c++11 中幾個違背直覺的地方, 雖說有少許錯誤, 但是還是值得思考的: http://url.cn/GYElGe
11 中引入了正則表達式, 這是一個卓越的特性, 但是本書沒有提及也沒有介紹. 請參考: http://www.cnblogs.com/walfud/articles/2846227.html 中搜索 "regular expression".