A Morden C++ —— C++11新特性指南

不知不覺C++1x,也就是之前的C++0x已經基本達到工業使用的成熟度了,各個編譯器的實現也很完整了(LLVM Clang 4.0+, VS 2010+, gcc 4.5+)。前兩天把項目裏的編譯選項從c++98提高到了c++1x,過程很平滑,徹底向下兼容。整理了一下C++1x的新特性,有不少不錯的feature,充分體現了C++的設計原則:「把語法上對程序員的限制減到最小」javascript

  1. lambda表達式 ref

killer feature,簡單地說就是閉包、匿名函數。沒有lambda的時候,實現回調須要使用仿函數Functor(一個重載了()的類),或者用一個對象指針加一個函數指針來模擬。兩種方法都沒法直接在使用處定義函數,語法上很是囉嗦,並且對於參數的處理也很是不靈活。lambda大大改善了這個問題。html

格式:[capture] (parameter list) -> return type { function body }
// 刪除集合中小於5的值
c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < 5; } ), c.end());
  1. 括號初始化列表 std::initializer_list ref

原來只有數組和結構體在初始化的時候才能用的{1,3,4,5}這樣的字面量,如今會被轉化成std::initializer_list<T>類型,這使得任意函數均可以接收這樣的參數,好比標準庫容器的構造函數。java

vector<int> a_int_vector = {1, 2, 3, 4};
a_int_vector.add({1, 2, 3, 4});
  1. 代理構造函數

簡單說就是構造函數能夠互相調用,原來若是多個構造函數有不少相同邏輯只能挪到一個init方法裏。如今能夠如此實現:c++

class Point {
private: int _x, _y;
public:
    Point() { Point(0, 0) }
    Point(int x) { Point(x, 0); }
    Point(int x, int y) _x(x), _y(y) { }
}
  1. 字符串字面量加強

  • unicode string
  • raw string literals (回想起之前在c++代碼裏嵌入javascript的經歷,淚流滿面)
u8"This is a Unicode Character: \u2018."
u"This is a bigger Unicode Character: \u2018."
U"This is a Unicode Character: \U00002018."
string path = R"(C:\Program Files\A B\a.exe)";
string html = R"(<a href="http://a.com/"></a>)"; // 注意:連引號都不須要轉義,C#弱爆了
  1. 用戶定義字面量 ref

一個帶來無限想像的特性程序員

inline constexpr long double operator "" _deg (long double deg) {
      return deg*3.141592/180;
}
double x = 90.0_deg; // x = 1.570796

long double operator "" _s  (long double s) { return s; }
long double operator "" _ms (long double s) { return s * 1E3L; }
long double operator "" _us (long double s) { return s * 1E6L; }
std::cout << 1.0_s  << std::endl; // 1
std::cout << 1.0_ms << std::endl; // 1000
std::cout << 1.0_us << std::endl; // 1000000
  1. 右值引用和move語義

這個特性理解起來比較繁瑣,主要的思想是經過充分利用右值(臨時變量),來加速和避免沒必要要的對象構造,典型的應用是「完美轉發」,下面的兩篇文章講的比較清楚。算法

  1. 強類型枚舉

這個相對比較雞肋數組

// RED GREEN BLUE不會被暴露到全局namespace,而且不能被隱式轉換成int
enum class Color {RED, GREEN, BLUE};  
namespace flags { // 若是是二進制標記位 不如實現成這樣
    constexpr int a = 1,
                  b = 1<<1,
                  c = 1<<2;
}
  1. 可變參數模版和參數打包

典型的用途之一就是後面會提到的std::tuple,或者像下面同樣實現一個log方法,這篇blog中有不錯的例子和講解。安全

template<typename First, typename... Other>  // ...的學名叫Parameter Pack
void log(First value, Other... remaining) {
    std::cout << value << ",";
    log(remaining);
}
template<typename T>
void log(T t) {
    std::cout << t << std::endl;
}
  1. constexpr ref

使得函數的返回值能夠具備const語義。申明爲constexpr的函數,若是調用的時候傳入的參數是常量表達式,那麼其返回值會在編譯期被計算,從而也是const的(c++編譯器有向操做系統發展的趨勢)。多線程

constexpr int exp(int a, int b) { 
    int x = a; 
    for (int i = 1; i < b; ++i) {
        x *= a;
    }
    return x;
}
const int a = exp(2, 10); // 1024 在編譯期被計算
  1. 新關鍵字

  • auto it=a_vector.begin(); 自動類型推導
  • decltype(&myfunc) a; 聲明一個和x類型相同的變量a; ref
  • void func() override; 和java中的override語義相似,表示重載父類virtual方法,避免意外覆蓋
  • constexpr 編譯期常量約定
  • nullptr 類型安全的空指針,代替NULL宏,ref
  • class A final 禁止類被繼承,沒有相似java做用在變量聲明上的imutable語義
  • struct alignas(16) sse_t { float sse_data[4]; } 申明對齊方式
  • long long int 類型,至少64位的整型,C99裏已經標準化了
  1. 標準庫中的新東西

std::tuple<int, int, int> fun() { return std::make_tuple(1,2,3); };
int a, b, c;
std::tie(a,b,c)=fun(); // 將tuple展開到三個變量中,與PHP中的list用法很是像
std::cout << a << b << c; // 123
  • 新增算法 all_of any_of none_of copy_if iota generate
std::list<int> l {1, 2, 3, 4, 5};
std::all_of (v.cbegin(), v.cend(), [](int i){ return i < 6; }); // true
std::none_of(v.cbegin(), v.cend(), [](int i){ return i > 5; }); // true
std::any_of (v.cbegin(), v.cend(), [](int i){ return i = 2; }); // true
std::iota(l.begin(), l.end(), -1); // {0, 1, 2, 3, 4}
    
std::vector<int> v(10);
std::generate(v.begin(), v.end(), std::rand)); // 隨機填充
  • 可擴展的隨機數工具 ref:由engine、engine adapter、number generator和distibution組成

參考

原文見個人blog http://mainloop.cc/2013/10/cp...閉包

相關文章
相關標籤/搜索