转自: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. java.lang.NoSuchMethodError: org.springframework.beans.factory.annotation.InjectionMetadata.<init>(Ljava/lang/Class;)V

    相应我,是因为你SPRING MVC的包没有加全.你可以新建一个WEB项目.加入SPRING 3.0 的所有包.主要是WEB类的.就可以解决这个问题了.关键就是少包.特别是你的项目原来是SRPING ...

  2. aop郁闷错误

    很郁闷的错误,终于解决了: <aop:config>  <aop:aspect ref="log">   <aop:pointcut id=" ...

  3. linux下gitflow辅助工具安装和使用

    gitflow是一个确保nvie推荐的git branch分支策略最佳模型得到有效实施的辅助工具.它作为git的一个子命令而存在. http://nvie.com/posts/a-successful ...

  4. [ionic开源项目教程] - 第15讲 ionic用户个人中心登录注册的实现

    第15讲 ionic用户个人中心登录注册的实现 这一讲包括登陆.注册.个人中心.个人资料页面的实现. 在一讲的改动有四个地方: 在config.js里配置接口地址 完善个人中心的服务层(service ...

  5. POJ 1988 Cube Stacking

    题意:有编号为1~N的N个小木块,有两种操作 M x y 将木块x所在的堆放到木块y所在的堆的上面 C x 询问木块x下面有多少块木块 代码巧妙就巧妙在GetParent函数中在进行路径压缩的同时,也 ...

  6. 51nod1262 扔球

    相关讨论里的答案:(by mint_my ) 1.反弹n次,那起点S,每次反弹点,终点S共连接n+1条边,那么原问题变为从S走n+1条边回到S,为令n=n+12.设步长为a条边,gcd(a,n)==1 ...

  7. BZOJ3674: 可持久化并查集加强版

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3674 题解:主要是可持久化的思想.膜拜了一下hzwer的代码后懂了. 其实本质是可持久化fa数 ...

  8. mysql里group by按照分组里的内容的排序

    得到一张表里按u_id分组,按count(id)排序,每个分组的pub_time最大的哪些记录,只取count(id)最大的4条 select a.u_id,a.name,a.u_name,a.id, ...

  9. 【自动化测试】Selenium - 定位

    http://easonhan007.github.io/selenium/2014/01/10/se-secret_01/ 你应该遵照这个规则来选择定位元素的属性.name > id > ...

  10. MVC路由调试工具RouteDebug

    环境 MVC3 路由注册 入口简单,在Global.asax文件RegisterRoutes方法中. 当为我们的应用程序注册多个路由后,由于注册不当,得不到预期的结果.为什么会发生这种情况,请求具体走 ...