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 };
相关文章
相关标签/搜索