C++ 11 Lambda表達式、auto、function、bind、final、override

接觸了cocos2dx 3.0,就必須得看C++ 11了。有分享過帖子:【轉帖】漫話C++0x(四) —- function, bind和lambda。其實最後的Lambda沒太怎麼看懂。html

看不懂不要緊,會用就行。惋惜是連用都要思考半天。其實,查找根源是定義沒有搞明白。ios

之後買東西,用以前,先看說明書纔是必要的。程序員

---------------------------------開始正文粘貼-----------------------------------------express

 

1、Lambda表達式編程

 

 C++ 11中的Lambda表達式用於定義並建立匿名的函數對象,以簡化編程工做。Lambda的語法形式以下:ide

lambda 表達式的結構化元素

      如上圖所示Lambda分爲如下幾個部分:一、 [函數對象參數] ,二、(操做符重載函數參數), 三、mutable, 四、exception聲明, 五、->返回值類型, 六、{函數體}。
      固然也能夠認爲Lambda主要分爲五個部分:[函數對象參數]、(操做符重載函數參數)、mutable或exception聲明、->返回值類型、{函數體}。函數

下面分別進行介紹。post


     一、[函數對象參數],標識一個Lambda的開始,這部分必須存在,不能省略。函數對象參數是傳遞給編譯器自動生成的函數對象類的構造函數的。函數對象參數只能使用那些到定義Lambda爲止時Lambda所在做用範圍內可見的局部變量(包括Lambda所在類的this)。函數對象參數有如下形式:
           一、空。沒有使用任何函數對象參數。
           二、=。函數體內可使用Lambda所在做用範圍內全部可見的局部變量(包括Lambda所在類的this),而且是值傳遞方式(至關於編譯器自動爲咱們按值傳遞了全部局部變量)。
           三、&。函數體內可使用Lambda所在做用範圍內全部可見的局部變量(包括Lambda所在類的this),而且是引用傳遞方式(至關於編譯器自動爲咱們按引用傳遞了全部局部變量)。
           四、this。函數體內可使用Lambda所在類中的成員變量。
           五、a。將a按值進行傳遞。按值進行傳遞時,函數體內不能修改傳遞進來的a的拷貝,由於默認狀況下函數是const的。要修改傳遞進來的a的拷貝,能夠添加mutable修飾符。
           六、&a。將a按引用進行傳遞。
           七、a, &b。將a按值進行傳遞,b按引用進行傳遞。
           八、=,&a, &b。除a和b按引用進行傳遞外,其餘參數都按值進行傳遞。
           九、&, a, b。除a和b按值進行傳遞外,其餘參數都按引用進行傳遞。
      二、(操做符重載函數參數),標識重載的()操做符的參數,沒有參數時,這部分能夠省略。參數能夠經過按值(如:(a,b))和按引用(如:(&a,&b))兩種方式進行傳遞。
      三、mutable或exception聲明,這部分能夠省略。按值傳遞函數對象參數時,加上mutable修飾符後,能夠修改按值傳遞進來的拷貝(注意是能修改拷貝,而不是值自己)。exception聲明用於指定函數拋出的異常,如拋出整數類型的異常,可使用throw(int)。
      四、->返回值類型,標識函數返回值的類型,當返回值爲void,或者函數體中只有一處return的地方(此時編譯器能夠自動推斷出返回值類型)時,這部分能夠省略。
      5、{函數體},標識函數的實現,這部分不能省略,但函數體能夠爲空。學習

其實學習Lambda最好的資料仍是MSDN,傳送門:Lambda 表達式語法http://msdn.microsoft.com/zh-cn/library/dd293603.aspxui

注意文中有一句:

Visual Studio 經過添加如下特性來增添 C++11 lambda 功能:

  • 無狀態 lambda 可經過任意調用約定徹底轉換爲函數指針。

  • 只要全部返回語句具備相同的類型,會自動推導比 { return expression; } 更復雜的 lambda 主體的返回類型。(這一特性現包含在擬建的 C++14 標準中。)

 

2、auto 關鍵字

 

C++ 11中引入的auto主要有兩種用途:自動類型推斷和返回值佔位

 

auto自動類型推斷,用於從初始化表達式中推斷出變量的數據類型。經過auto的自動類型推斷,能夠大大簡化咱們的編程工做。

所以,Lambda表達式和auto關鍵字合體以後,就能夠很是方便的用相似以下的代碼了:

auto a; // 錯誤,沒有初始化表達式,沒法推斷出a的類型  
auto int a = 10 // 錯誤,auto臨時變量的語義在C++ 11中已不存在  
auto a = 10  
auto c = 'A' 
auto s("hello");  
vector<int> vctTemp;  
auto it = vctTemp.begin();  
auto ptr = [](){ cout << "hello world" << endl; };
1 template <typename T1, typename T2>
2 auto compose(T1 t1, T2 t2) -> decltype(t1 + t2)
3 {
4    return t1+t2;
5 }
6 auto v = compose(2, 3.14); // v's type is double

注意:auto並不能做爲函數的返回類型,可是能用auto去代替函數的返回類型,固然,在這種狀況下,函數必須有返回值才能夠。auto不會告訴編譯器去推斷返回值的實際類型,它會通知編譯器在函數的末段去尋找返回值類型。在上面的那個例子中,函數返回值的構成是由T1類型和T2類型的值,通過+操做符以後決定的。

自動化推導decltype

關於 decltype 是一個操做符,其能夠評估括號內表達式的類型,其規則以下:

  1. 若是表達式e是一個變量,那麼就是這個變量的類型。
  2. 若是表達式e是一個函數,那麼就是這個函數返回值的類型。
  3. 若是不符合1和2,若是e是左值,類型爲T,那麼decltype(e)是T&;若是是右值,則是T。

原文給出的示例以下,咱們能夠看到,這個讓的確咱們的定義變量省了不少事。

decltype(&myfunc) pfunc = 0;
typedef decltype(&A::func1) type;

3、std::function

類模版 std::function是一種通用、多態的函數封裝。std::function的實例能夠對任何能夠調用的目標進行存儲、複製、和調用操做,這些目標包括函數、lambda表達式、綁定表達式、以及其它函數對象等。

用法示例:

①保存自由函數

1 void printA(int a)
2 {
3     cout<<a<<endl;
4 }
5 
6  std::function<void(int a)> func;
7  func = printA;
8  func(2);

②保存lambda表達式

 1 std::function<void()> func_1 = [](){cout<<"hello world"<<endl;}; 2 func_1(); 

運行輸出:hello world

③保存成員函數

 1 struct Foo {
 2     Foo(int num) : num_(num) {}
 3     void print_add(int i) const { cout << num_+i << '\n'; }
 4     int num_;
 5 };
 6 
 7  // 保存成員函數
 8     std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
 9     Foo foo(2);
10     f_add_display(foo, 1);

運行輸出: 3

4、bind

bind是一組用於函數綁定的模板。在對某個函數進行綁定時,能夠指定部分參數或所有參數,也能夠不指定任何參數,還能夠調整各個參數間的順序。對於未指定的參數,可使用佔位符_一、_二、_3來表示。_1表示綁定後的函數的第1個參數,_2表示綁定後的函數的第2個參數,其餘依次類推。

下面經過程序例子瞭解一下用法:

 1 #include <iostream>
 2 using namespace std;
 3 class A
 4 {
 5 public:
 6     void fun_3(int k,int m)
 7     {
 8         cout<<k<<" "<<m<<endl;
 9     }
10 };
11 
12 void fun(int x,int y,int z)
13 {
14     cout<<x<<"  "<<y<<"  "<<z<<endl;
15 }
16 
17 void fun_2(int &a,int &b)
18 {
19     a++;
20     b++;
21     cout<<a<<"  "<<b<<endl;
22 }
23 
24 int main(int argc, const char * argv[])
25 {
26     auto f1 = bind(fun,1,2,3); //表示綁定函數 fun 的第一,二,三個參數值爲: 1 2 3
27     f1(); //print:1  2  3
28     
29     auto f2 = bind(fun, placeholders::_1,placeholders::_2,3);
30     //表示綁定函數 fun 的第三個參數爲 3,而fun 的第一,二個參數分別有調用 f2 的第一,二個參數指定
31     f2(1,2);//print:1  2  3
32     
33     auto f3 = bind(fun,placeholders::_2,placeholders::_1,3);
34     //表示綁定函數 fun 的第三個參數爲 3,而fun 的第一,二個參數分別有調用 f3 的第二,一個參數指定
35     //注意: f2  和  f3 的區別。
36     f3(1,2);//print:2  1  3
37     
38     
39     int n = 2;
40     int m = 3;
41     
42     auto f4 = bind(fun_2, n,placeholders::_1);
43     f4(m); //print:3  4
44 
45     cout<<m<<endl;//print:4  說明:bind對於不事先綁定的參數,經過std::placeholders傳遞的參數是經過引用傳遞的
46     cout<<n<<endl;//print:2  說明:bind對於預先綁定的函數參數是經過值傳遞的
47     
48     
49     A a;
50     auto f5 = bind(&A::fun_3, a,placeholders::_1,placeholders::_2);
51     f5(10,20);//print:10 20
52     
53     std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
54     fc(10,20);//print:10 20
55     
56     return 0;
57 }

5、nullptr -- 空指針標識

空指針標識(nullptr)(其本質是一個內定的常量)是一個表示空指針的標識,它不是一個整數。(譯註:這裏應該與咱們經常使用的NULL宏相區別,雖然它們都是用來表示空置針,但NULL只是一個定義爲常整數0的宏,而nullptr是C++0x的一個關鍵字,一個內建的標識符。下面咱們還將看到nullptr與NULL之間更多的區別。)

 1     char* p = nullptr;
 2     int* q = nullptr;
 3     char* p2 = 0;           //這裏0的賦值仍是有效的,而且p=p2
 4 
 5     void f(int);
 6     void f(char*);
 7 
 8     f(0);         //調用f(int)
 9     f(nullptr);   //調用f(char*)
10 
11     void g(int);
12     g(nullptr);       //錯誤:nullptr並非一個整型常量
13     int i = nullptr;  //錯誤:nullptr並非一個整型常量
14 (譯註:實際上,咱們這裏能夠看到nullptr和NULL二者本質的差異,NULL是一個整型數0,而nullptr能夠當作是一個空指針。)

 

6、final 和 override

二者都是用在對於繼承體系的控制。

final

用來標明被這個修飾符修飾的class/struct和虛函數已是最終版本,沒法被進一步繼承.

override

override關鍵字用來表示在子類的函數必定重載自基類的同名同性質的虛函數或者純虛函數,不然沒法被編譯.

 1 class Base
 2 {
 3 public:
 4     virtual void test(){}
 5     virtual void test2(int i) {}
 6     virtual void test3() const {}
 7 };
 8 
 9 class D1:public Base
10 {
11     void test() override {}  //編譯正確
12     void test2(float i) override {} //編譯錯誤,參數不一致
13     void test3() override {} //編譯錯誤,函數常量性不一致
14     void test4() override {} //編譯錯誤,並非重載父類虛函數
15 };

須要注意的一點是,final和override二者頗有可能在C++ 98的代碼裏面被程序員大量的用在其餘地方命名,所以C++ 11爲了保持和以前代碼的兼容性,因此這兩個標記只有在修飾class/struct和函數的時候,纔會被當成關鍵字。也就是說,在其餘地方依然可使用這兩個字符命名成變量/函數/類/結構體.

好比:

 1 class Base
 2 {
 3 public:
 4     virtual void test()
 5     {
 6         int final = 1;
 7     }
 8     virtual void test2(int i) {}
 9     virtual void test3() const {}
10     virtual void override();
11 };
相關文章
相關標籤/搜索