转自:http://blog.csdn.net/crayondeng/article/details/18563121

一、Lambda表达式

C++ 11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。Lambda的语法形式如下:

              [函数对象参数] (操作符重载函数参数) mutable或exception声明 ->返回值类型 {函数体}

      可以看到,Lambda主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable或exception声明、->返回值类型、{函数体}。下面分别进行介绍。

     一、[函数对象参数],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

           1、空。没有使用任何函数对象参数。

           2、=。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。

           3、&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。

           4、this。函数体内可以使用Lambda所在类中的成员变量。

           5、a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。

           6、&a。将a按引用进行传递。

           7、a, &b。将a按值进行传递,b按引用进行传递。

           8、=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。

           9、&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

      二、(操作符重载函数参数),标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

      三、mutable或exception声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)。

      四、->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

      五、{函数体},标识函数的实现,这部分不能省略,但函数体可以为空。

下面给出一个例子:

  1. class CTest
  2. {
  3. public:
  4. CTest() : m_nData(20) { NULL; }
  5. void TestLambda()
  6. {
  7. vector<int> vctTemp;
  8. vctTemp.push_back(1);
  9. vctTemp.push_back(2);
  10. // 无函数对象参数,输出:1 2
  11. {
  12. for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; });
  13. }
  14. // 以值方式传递作用域内所有可见的局部变量(包括this),输出:11 12
  15. {
  16. int a = 10;
  17. for_each(vctTemp.begin(), vctTemp.end(), [=](int v){ cout << v+a << endl; });
  18. }
  19. // 以引用方式传递作用域内所有可见的局部变量(包括this),输出:11 13 12
  20. {
  21. int a = 10;
  22. for_each(vctTemp.begin(), vctTemp.end(), [&](int v)mutable{ cout << v+a << endl; a++; });
  23. cout << a << endl;
  24. }
  25. // 以值方式传递局部变量a,输出:11 13 10
  26. {
  27. int a = 10;
  28. //注意:在lambda表达式中的 a 只是一个拷贝,添加mutable之后,可以修改a的值,但只是修改拷贝的a
  29. for_each(vctTemp.begin(), vctTemp.end(), [a](int v)mutable{ cout << v+a << endl; a++; });
  30. cout << a << endl;
  31. }
  32. // 以引用方式传递局部变量a,输出:11 13 12
  33. {
  34. int a = 10;
  35. for_each(vctTemp.begin(), vctTemp.end(), [&a](int v){ cout << v+a << endl; a++; });
  36. cout << a << endl;
  37. }
  38. // 传递this,输出:21 22
  39. {
  40. for_each(vctTemp.begin(), vctTemp.end(), [this](int v){ cout << v+m_nData << endl; });
  41. }
  42. // 除b按引用传递外,其他均按值传递,输出:11 12 17
  43. {
  44. int a = 10;
  45. int b = 15;
  46. for_each(vctTemp.begin(), vctTemp.end(), [=, &b](int v){ cout << v+a << endl; b++; });
  47. cout << b << endl;
  48. }
  49. // 操作符重载函数参数按值传递,输出:1 2
  50. {
  51. for_each(vctTemp.begin(), vctTemp.end(), [](int v){ cout << v << endl; });
  52. }
  53. // 操作符重载函数参数按引用传递,输出:2 3
  54. {
  55. for_each(vctTemp.begin(), vctTemp.end(), [](int &v){ v++; cout<<v<<endl;});
  56. }
  57. // 空的Lambda表达式
  58. {
  59. [](){}();
  60. []{}();
  61. }
  62. }
  63. private:
  64. int m_nData;
  65. };

二、auto 关键字

C++ 11中引入的auto主要有两种用途:自动类型推断和返回值占位。

auto自动类型推断,用于从初始化表达式中推断出变量的数据类型。通过auto的自动类型推断,可以大大简化我们的编程工作。下面是一些使用auto的例子。

  1. auto a; // 错误,没有初始化表达式,无法推断出a的类型
  2. auto int a = 10 // 错误,auto临时变量的语义在C++ 11中已不存在
  3. auto a = 10
  4. auto c = 'A'
  5. auto s("hello");
  6. vector<int> vctTemp;
  7. auto it = vctTemp.begin();
  8. auto ptr = [](){ cout << "hello world" << endl; };

使用auto经常意味着较少的代码量(除非你需要的类型是int这种只有一个单词的)。当你想要遍历STL容器中元素的时候,想一想你会怎么写迭代器代码,老式的方法是用很多typedef来做,而auto则会大大简化这个过程。

  1. std::map<std::string, std::vector<int>> map;
  2. for(auto it = begin(map); it != end(map); ++it)
  3. {
  4. }

另外,在使用模板技术时,如果某个变量的类型依赖于模板参数,不使用auto将很难确定变量的类型(使用auto后,将由编译器自动进行确定)。

下面是一个具体的例子。

  1. template <class T, class U>
  2. void Multiply(T t, U u)
  3. {
  4. auto v = t*u;
  5. }
auto返回值占位,主要与decltype配合使用,用于返回值类型后置时的占位。
  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。

原文给出的示例如下,我们可以看到,这个让的确我们的定义变量省了很多事。

  1. const vector<int> vi;
  2. typedef decltype (vi.begin()) CIT;
  3. CIT another_const_iterator;

还有一个适合的用法是用来typedef函数指针,也会省很多事。比如:

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

三、std::function

类模版 std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标进行存储、复制、和调用操作,这些目标包括函数、lambda表达式、绑定表达式、以及其它函数对象等。

用法示例:

①保存自由函数

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

运行输出: 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. std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
  8. Foo foo(2);
  9. f_add_display(foo, 1);

运行输出: 3

四、bind

bind是一组用于函数绑定的模板。在对某个函数进行绑定时,可以指定部分参数或全部参数,也可以不指定任何参数,还可以调整各个参数间的顺序。对于未指定的参数,可以使用占位符_1、_2、_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. void fun(int x,int y,int z)
  12. {
  13. cout<<x<<"  "<<y<<"  "<<z<<endl;
  14. }
  15. void fun_2(int &a,int &b)
  16. {
  17. a++;
  18. b++;
  19. cout<<a<<"  "<<b<<endl;
  20. }
  21. int main(int argc, const char * argv[])
  22. {
  23. auto f1 = bind(fun,1,2,3); //表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
  24. f1(); //print:1  2  3
  25. auto f2 = bind(fun, placeholders::_1,placeholders::_2,3);
  26. //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别有调用 f2 的第一,二个参数指定
  27. f2(1,2);//print:1  2  3
  28. auto f3 = bind(fun,placeholders::_2,placeholders::_1,3);
  29. //表示绑定函数 fun 的第三个参数为 3,而fun 的第一,二个参数分别有调用 f3 的第二,一个参数指定
  30. //注意: f2  和  f3 的区别。
  31. f3(1,2);//print:2  1  3
  32. int n = 2;
  33. int m = 3;
  34. auto f4 = bind(fun_2, n,placeholders::_1);
  35. f4(m); //print:3  4
  36. cout<<m<<endl;//print:4  说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的
  37. cout<<n<<endl;//print:2  说明:bind对于预先绑定的函数参数是通过值传递的
  38. A a;
  39. auto f5 = bind(&A::fun_3, a,placeholders::_1,placeholders::_2);
  40. f5(10,20);//print:10 20
  41. std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);
  42. fc(10,20);//print:10 20
  43. return 0;
  44. }

五、nullptr -- 空指针标识

空指针标识(nullptr)(其本质是一个内定的常量)是一个表示空指针的标识,它不是一个整数。(译注:这里应该与我们常用的NULL宏相区别,虽然它们都是用来表示空置针,但NULL只是一个定义为常整数0的宏,而nullptr是C++0x的一个关键字,一个内建的标识符。下面我们还将看到nullptr与NULL之间更多的区别。)

    char* p = nullptr;

    int* q = nullptr;

    char* p2 = 0;           //这里0的赋值还是有效的,并且p=p2



    void f(int);

    void f(char*);



    f(0);         //调用f(int)

    f(nullptr);   //调用f(char*)



    void g(int);

    g(nullptr);       //错误:nullptr并不是一个整型常量

    int i = nullptr;  //错误:nullptr并不是一个整型常量

(译注:实际上,我们这里可以看到nullptr和NULL两者本质的差别,NULL是一个整型数0,而nullptr可以看成是一个空指针。)

六、final 和 override

两者都是用在对于继承体系的控制。

final

用来标明被这个修饰符修饰的class/struct和虚函数已经是最终版本,无法被进一步继承.

[html] view
plain
copy

  1. class Base final
  2. {
  3. public:
  4. virtual void test(){}
  5. };
  6. class D1:public Base
  7. {
  8. void test(){}
  9. };

如这个例子,Base类被final修饰,表示其已经无法被继承,编译器会提示如下错误:error C3246: ‘D1′ : cannot inherit from ‘Base’ as it has been declared as ‘final’

再看另外一个例子:

  1. class Base
  2. {
  3. public:
  4. virtual void test(){}
  5. };
  6. class D1:public Base
  7. {
  8. void test() final {}
  9. };
  10. class D2 :public D1
  11. {
  12. void test(){}
  13. };

此时虚函数test在D1被final标识,已经表明其是最终版本,无法进一步继承.

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. class D1:public Base
  9. {
  10. void test() override {}  //编译正确
  11. void test2(float i) override {} //编译错误,参数不一致
  12. void test3() override {} //编译错误,函数常量性不一致
  13. void test4() override {} //编译错误,并不是重载父类虚函数
  14. };

需要注意的一点是,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. };

这是正确的.


C++11 语法记录的更多相关文章

  1. MarkDown常用语法记录

    目录 1. 斜体和粗体 2. 分级标题 3. 超链接 3.1 行内式(推荐) 3.2 行外式 3.3 自动链接 4. 锚点 5. 列表 5.1无序列表 5.2有序列表 6. 引用 7. 插入图像 8. ...

  2. MarkDown基础语法记录

    基础语法记录,其中有一些博客园暂不支持 <!--标题--> # 一级标题 # ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 一级标题 ...

  3. ABAP 新语法记录(一)

    原文链接:https://www.cnblogs.com/learnning/p/10647174.html 主要内容 内联声明 构造表达式 内表操作 Open SQL 其他 本文列出了ABAP新语法 ...

  4. proto3语法记录

    protobuf 是谷歌的语言无关,平台无关,可扩展的,高效的结构化数据序列化机制,比xml和json的序列化的速度更快,此处记录一下 proto3 的语法,防止以后忘记. 注意:proto3 语法需 ...

  5. SQL 常用语法记录

    SQL语法 注意:SQL 对大小写不敏感 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). 数据操作语言 (DML) SQL (结构化查询语言)是用于执行查询的语 ...

  6. Markdown的基本语法记录

    1.标题 示例代码: # 标题1 ## 标题2 ### 标题3 #### 标题4 ##### ... 效果: 标题1 标题2 标题3 标题4 ... 注:# 后面应保持空格 2. 分级标题 示例代码: ...

  7. shell基本语法记录

    Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个 ...

  8. [C/C++11语法]_[0基础]_[lamba 表达式介绍]

    场景 lambda 表达式在非常多语言里都有一席之地,由于它的原因,能够在函数里高速定义一个便携的函数,或者在函数參数里直接高速构造和传递. 它能够说是匿名函数对象,一般仅仅适用于某个函数内,仅仅做暂 ...

  9. MarkDown语法记录,还在用word,txt编写项目文档吗?

    开始之前 是不是在github上看项目的时候第一眼就要看项目介绍? 是不是经常在某些项目的代码里面看到一个README.MD文档 却不知道怎么写? 你是不是不知道,反正我是的. 作为一个程序员,可能写 ...

随机推荐

  1. POJ 2236 (简单并查集) Wireless Network

    题意: 有n个电脑坏掉了,分别给出他们的坐标 有两种操作,可以O x表示修好第x台电脑,可以 S x y表示x y是否连通 两台电脑的距离不超过d便可连通,两台电脑是连通的可以直接连通也可以间接通过第 ...

  2. HDU 2870 Largest Submatrix

    这三道题的关系是这样的,1505是1506的加强版,2870又是1505的加强版 如果按照上面由简到易的顺序来做的话,还是很简单的 这道题的思想就是 枚举+DP 因为某些字符可以变值,所以我们枚举a, ...

  3. 快速查询Python脚本语法

    /********************************************************************* * 快速查询Python脚本语法 * 说明: * Char ...

  4. 新版Windows Azure CDN管理门户正式上线

    经过产品团队的不懈努力,新版Windows Azure CDN管理门户在经过了有限开放预览之后,已经正式上线并开放给所有用户. 新版Windows Azure CDN管理门户经过全新的设计,除了在使用 ...

  5. wxWidgets简单的多线程

    #include <wx/wx.h> #include <wx/thread.h> #include <wx/event.h> #include <wx/pr ...

  6. mysql 优化analyze table

    Analyze Table MySQL 的Optimizer(优化元件)在优化SQL语句时,首先需要收集一些相关信息,其中就包括表的cardinality(可以翻译为“散列程度”),它表示某个索引对应 ...

  7. JVM——垃圾收集算法

    1.标记-清除算法 最基础的收集算法,如其名,算法为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 两个不足: 1)效率问题,标记和清除两个过程的效率 ...

  8. [Android] 关于系统工具栏和全屏沉浸模式

    随着应用程序的一些深入设计,大家总想要更好的界面和体验,所以有些东西并不能只是知道方法就结束了,是得要去深入研究研究的.通过这个过程我觉得,从应用层面来讲,想实现一个功能很简单,但若想实现的好,就要去 ...

  9. nodejs、sass、backbone等api地址

    1.nodejs Node.js v4.2.4 手册 & 文档 2.sass Sass (3.4.21) 中文文档 3.backbone Backbone.js(1.1.2) API中文文档 ...

  10. Python time mktime()方法

    描述 Python time mktime() 函数执行与gmtime(), localtime()相反的操作,它接收struct_time对象作为参数,返回用秒数来表示时间的浮点数. 如果输入的值不 ...