转自: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. Navicat数据存放位置和备份数据库路径设置

    navicat的数据库存放位置在什么地方?带着这样的疑问,我们去解决问题,navicat是默认安装,mysql也是默认安装,数据库存在默认用户所在的目录下面. 安装MySQL时,请选择“Custom” ...

  2. Windows 下搭建LDAP服务器

    五一闲来没事,加上项目正在进行UAT.抽空研究了一下LDAP相关知识.随手做一个记录. 为了方便阅读还是先介绍一下什么是LDAP? 前言.Lightweight Directory Access Pr ...

  3. UVa 10020 (最小区间覆盖) Minimal coverage

    题意: 数轴上有n个闭区间[ai, bi],选择尽量少的区间覆盖一条指定线段[0, m] 算法: [start, end]为已经覆盖到的区间 这是一道贪心 把各个区间先按照左端点从小到大排序,更新st ...

  4. HDU 1080 Human Gene Functions

    最长公共子序列的变形 题目大意:给出两个基因序列,求这两个序列的最大相似度. 题目中的表格给出了两两脱氧核苷酸的相似度. 状态转移方程为: dp[i][j] = max(dp[i-1][j]+Simi ...

  5. 使用Java API创建(create),查看(describe),列举(list),删除(delete)Kafka主题(Topic)

    使用Kafka的同学都知道,我们每次创建Kafka主题(Topic)的时候可以指定分区数和副本数等信息,如果将这些属性配置到server.properties文件中,以后调用Java API生成的主题 ...

  6. BZOJ 3306 树

    dfs序建线段树+分类讨论+写的有点长. #include<iostream> #include<cstdio> #include<cstring> #includ ...

  7. CodeIgniter的缓存设置

    数据库缓存 数据库缓存类允许你把数据库查询结果保存在文本文件中以减少数据库访问. 激活缓存需要三步: 在服务器上创建一个可写的目录以便保存缓存文件. 在文件 application/config/da ...

  8. scala学习笔记(1):基本语法与容器

    1 var 可变,val 不可变 var (a,b) = (10,20) 分别对a,b赋值 a=10, b=20 var a,b = (10,20)则 是a=(10,20) b=(10,20) 2 L ...

  9. 如何用HTML5+PhoneGap写个Path项目

    最近Path这个应用很火爆,网上也出现了不少仿Path菜单的项目.即使在原生APP里边,Path的效果也是非常赞的.我突然想,Web APP是不是也能做出类似Path那样的效果呢?于是就有了OPath ...

  10. android webview 遇到的问题:external/chromium/net/disk_cache/stat_hub.cc:216:

    今天也遇到这个问题,界面显示无法访问,Baidu吧,结果有些含糊其词,有的说加网络权限,我看了下我的, 有个 <uses-permission android:name="androi ...