19,C++——11

[复制链接]
发表于 2025-10-8 20:22:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目次

一、 C++11简介
二、 新增的列表初始化
三、 新增的STL容器
四、 简化声明
1,auto
2,decltype
3,nullptr
五、右值引用
1,左值引用和右值引用
2,两种引用的比力
3,左值引用的利用场景
4,右值引用的利用场景
5,完善转发
六、新增的类功能
1,原始默认成员函数
2,新增默认成员函数
3,关键字default
4,关键字delete
七、可变参数模板
1,语法格式
2,递归睁开
3,逗号表达式睁开
八、lambda表达式
1,数组元素排序
2,自界说类排序和lambda应用
3,lambda表达式的语法格式
4,捕获列表的语法格式
九、包装器
1,包装器的利用场景
2,包装器的语法格式
3,bind函数
点个赞吧!666


一、 C++11简介

在2003年C++标准委员会曾经提交了一份技能勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不外由于C++03(TC1)重要是对C++98标准中的弊端举行修复,语言的焦点部门则没有改动,因此人们风俗性的把两个标准归并称为C++98/03标准。
从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。相比于C++98/03,C++11则带来了数目可观的变革,此中包罗了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。
相比力而言,C++11能更好地用于体系开发和库开发、语法更加泛华和简单化、更加稳固和安全,不但功能更强盛,而且能提拔步调员的开发服从,公司现实项目开发中也用得比力多。
二、 新增的列表初始化

在C++98中,标准允许利用花括号{}对数组大概布局体元素,举行同一的列表初始值设定。
C++11中,扩大了用大括号括起的列表(初始化列表)的利用范围,使其可用于全部的内置范例和用户自界说的范例,利用初始化列表时,可添加等号(=),也可不添加。
C++11中,利用列表初始化,会主动调用构造函数初始化,还实用于new表达式。
  1. struct Point
  2. {
  3.         int _x;
  4.         int _y;
  5.         int _z;
  6. };
  7. class Date
  8. {
  9. public:
  10.         Date(int year, int month, int day)
  11.                 :_year(year)
  12.                 , _month(month)
  13.                 , _day(day)
  14.         {
  15.                 cout << "Date(int year, int month, int day)" << endl;
  16.         }
  17. private:
  18.         int _year;
  19.         int _month;
  20.         int _day;
  21. };
  22. void test1()
  23. {
  24.         // C++98
  25.         int arr1[] = { 1, 2, 3, 4, 5 };
  26.         int arr2[5] = { 0 };
  27.         Point p = { 1, 2 ,3 };
  28.         Date d1(2022, 1, 1);
  29.         // C++11
  30.         int arr3[]{ 1, 2, 3, 4, 5 };
  31.         int arr4[5]{ 0 };
  32.         Point q{ 1, 2 ,3 };
  33.         Date d2{ 2022, 1, 2 };
  34.         Date d3 = { 2022, 1, 3 };
  35.         int* pa = new int[4]{ 0 };
  36.        
  37. }
复制代码
三、 新增的STL容器

<array>和<forward_list>用的比力少,现实上最有用的是<unordered_map>和<unordered_set>
如果我们再细细去看会发现根本每个容器中都增长了一些C++11的方法,但是实在很多都是用得比力少的。
比如提供了cbegin和cend方法返回const迭代器等等,但是现实意义不大,由于begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操纵。


四、 简化声明

C++11提供了多种简化声明的方式,尤其是在利用模板时。
1,auto

在C++98中auto是一个存储范例的分析符,表明变量是局部主动存储范例,但是局部域中界说局部的变量默认就是主动存储范例,以是auto就没什么代价了。C++11中废弃auto原来的用法,将其用于实现主动范例腿断。如许要求必须举行表现初始化,让编译器将界说对象的范例设置为初始化值的范例。
  1. void test2()
  2. {
  3.         int i = 10;
  4.         auto p = &i;
  5.         auto pf = strcpy;
  6.         cout << typeid(p).name() << endl;
  7.         cout << typeid(pf).name() << endl;
  8.         map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
  9.         map<string, string>::iterator it = dict.begin();
  10.         auto e = dict.begin();
  11. }
复制代码
2,decltype

关键字decltype将变量的范例声明为表达式指定的范例。
  1. template<class T1, class T2>
  2. void F(T1 t1, T2 t2)
  3. {
  4.         decltype(t1 * t2) ret;
  5.         cout << typeid(ret).name() << endl; // ret的类型是int
  6. }
  7. void test3()
  8. {
  9.         F(1, 'a');
  10.         const int x = 1;
  11.         double y = 2.2;
  12.         decltype(x * y) ret;  // ret的类型是double
  13.         decltype(&x) p;       // p的类型是int*
  14.         cout << typeid(ret).name() << endl;
  15.         cout << typeid(p).name() << endl;
  16. }
复制代码
3,nullptr

由于C++中NULL被界说成字面量0,如许就大概回带来一些标题,由于0既能指针常量,又能体现整形常量。以是出于清晰和安全的角度思量,C++11中新增了nullptr,用于体现空指针。
  1. #ifndef NULL
  2. #ifdef __cplusplus
  3. #define NULL   0
  4. #else
  5. #define NULL   ((void *)0)
  6. #endif
复制代码
五、右值引用

1,左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,之前利用的引用就叫做左值引用,无论左值引用还是右值引用,都是给对象取别名。
左值是一个体现数据的表达式(如变量名或解引用的指针),我们可以获取它的所在+可以对它赋值,左值可以出现赋值符号的左边,右值不能出如今赋值符号左边。界说时const修饰符后的左值,不能给他赋值,但是可以取它的所在。
右值也是一个体现数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出如今赋值符号的右边,但是不能出现出如今赋值符号的左边,右值不能取所在。
  1. void test4()
  2. {
  3.         // 以下的p、b、c、*p都是左值
  4.         int* p = new int(0);
  5.         int b = 1;
  6.         const int c = 2;
  7.         // 以下几个是对上面左值的左值引用
  8.         int*& rp = p;
  9.         int& rb = b;
  10.         const int& rc = c;
  11.         int& pvalue = *p;
  12.         double x = 1.1, y = 2.2;
  13.         // 以下几个都是常见的右值
  14.         10;
  15.         x + y;
  16.         fmin(x, y);
  17.         // 以下几个都是对右值的右值引用
  18.         int&& rr1 = 10;
  19.         double&& rr2 = x + y;
  20.         double&& rr3 = fmin(x, y);
  21. }
复制代码
2,两种引用的比力

左值引用总结:
1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值。
右值引用总结:
1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。
  1. void test5()
  2. {
  3.         // 左值引用只能引用左值,不能引用右值。
  4.         int a = 10;
  5.         int& ra1 = a;            // ra为a的别名
  6.         // int& ra2 = 10;   // 编译失败,因为10是右值
  7.        
  8.         // const左值引用既可引用左值,也可引用右值。
  9.         const int& ra3 = 10;
  10.         const int& ra4 = a;
  11.         // 右值引用只能右值,不能引用左值。
  12.         int&& r1 = 10;
  13.         // error C2440: “初始化”: 无法从“int”转换为“int &&”
  14.         // message : 无法将左值绑定到右值引用
  15.         int a = 10;
  16.         int&& r2 = a;
  17.         // 右值引用可以引用move以后的左值
  18.         int&& r3 = std::move(a);
  19. }
复制代码
3,左值引用的利用场景

  1. void func1(bit::string s)
  2. {}
  3. void func2(const bit::string& s)
  4. {}
  5. int main()
  6. {
  7. bit::string s1("hello world");
  8. // func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值
  9. func1(s1);
  10. func2(s1);
  11. // string operator+=(char ch) 传值返回存在深拷贝
  12. // string& operator+=(char ch) 传左值引用没有拷贝提高了效率
  13. s1 += '!';
  14. return 0;
  15. }
复制代码
左值引用的短板:
但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能利用左值引用返回了, 只能利用传值返回。传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器大概是两次拷贝构造)。
4,右值引用的利用场景

右值引用可以补齐左值引用的短板。
移动构造本质是将参数右值的资源盗取过来,占位已有,那么就不消做深拷贝了,以是它叫做移动构造,就是盗取别人的资源来构造本身。
没有调用深拷贝的拷贝构造,而是调用了移动构造,移动构造中没有新开空间,拷贝数据,以是服从进步了。
不但仅有移动构造,另有移动赋值:
在string类中增长移动赋值函数,再去调用to_string(-6789),不外这次是将to_string(-6789)返回的右值对象赋值给ret1对象,这时调用的是移动构造。


  1. namespace ccc
  2. {
  3.         class string
  4.         {
  5.         public:
  6.                 typedef char* iterator;
  7.                 iterator begin()
  8.                 {
  9.                         return _str;
  10.                 }
  11.                 iterator end()
  12.                 {
  13.                         return _str + _size;
  14.                 }
  15.                 string(const char* str = "")
  16.                         :_size(strlen(str))
  17.                         , _capacity(_size)
  18.                 {
  19.                         //cout << "string(char* str)" << endl;
  20.                         _str = new char[_capacity + 1];
  21.                         strcpy(_str, str);
  22.                 }
  23.                 // s1.swap(s2)
  24.                 void swap(string& s)
  25.                 {
  26.                         ::swap(_str, s._str);
  27.                         ::swap(_size, s._size);
  28.                         ::swap(_capacity, s._capacity);
  29.                 }
  30.                 // 拷贝构造
  31.                 string(const string& s)
  32.                         :_str(nullptr)
  33.                 {
  34.                         cout << "string(const string& s) -- 拷贝构造(深拷贝)" << endl;
  35.                         string tmp(s._str);
  36.                         swap(tmp);
  37.                 }
  38.                 // 移动构造
  39.                 string(string&& s)
  40.                         :_str(nullptr)
  41.                         , _size(0)
  42.                         , _capacity(0)
  43.                 {
  44.                         cout << "string(string&& s) -- 移动构造" << endl;
  45.                         swap(s);
  46.                 }
  47.                 // 拷贝赋值重载
  48.                 string& operator=(const string& s)
  49.                 {
  50.                         cout << "string& operator=(string s) -- 拷贝赋值(深拷贝)" << endl;
  51.                         string tmp(s);
  52.                         swap(tmp);
  53.                         return *this;
  54.                 }
  55.                 // 移动赋值重载
  56.                 string& operator=(string&& s)
  57.                 {
  58.                         cout << "string& operator=(string&& s) -- 移动赋值" << endl;
  59.                         swap(s);
  60.                         return *this;
  61.                 }
  62.                 ~string()
  63.                 {
  64.                         //cout << "~string()" << endl;
  65.                         delete[] _str;
  66.                         _str = nullptr;
  67.                 }
  68.                 char& operator[](size_t pos)
  69.                 {
  70.                         assert(pos < _size);
  71.                         return _str[pos];
  72.                 }
  73.                 void reserve(size_t n)
  74.                 {
  75.                         if (n > _capacity)
  76.                         {
  77.                                 char* tmp = new char[n + 1];
  78.                                 strcpy(tmp, _str);
  79.                                 delete[] _str;
  80.                                 _str = tmp;
  81.                                 _capacity = n;
  82.                         }
  83.                 }
  84.                 void push_back(char ch)
  85.                 {
  86.                         if (_size >= _capacity)
  87.                         {
  88.                                 size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
  89.                                 reserve(newcapacity);
  90.                         }
  91.                         _str[_size] = ch;
  92.                         ++_size;
  93.                         _str[_size] = '\0';
  94.                 }
  95.                 //string operator+=(char ch)
  96.                 string& operator+=(char ch)
  97.                 {
  98.                         push_back(ch);
  99.                         return *this;
  100.                 }
  101.                 const char* c_str() const
  102.                 {
  103.                         return _str;
  104.                 }
  105.         private:
  106.                 char* _str;
  107.                 size_t _size;
  108.                 size_t _capacity; // 不包含最后做标识的\0
  109.         };
  110. }
  111. ccc::string to_string(int val)
  112. {
  113.         bool flag = true;
  114.         if (val < 0)
  115.         {
  116.                 flag = false;
  117.                 val = 0 - val;
  118.         }
  119.         ccc::string str;
  120.         while (val > 0)
  121.         {
  122.                 int x = val % 10;
  123.                 val = val / 10;
  124.                 str += ('0' + x);
  125.         }
  126.         if (flag == false)
  127.         {
  128.                 str += '-';
  129.         }
  130.         std::reverse(str.begin(), str.end());
  131.         return str;
  132. }
  133. void test5()
  134. {
  135.         // 编译器会优化,减少临时拷贝
  136.         ccc::string ret_1 = to_string(-2345);
  137.         cout << endl << endl;
  138.         // 左值引用,可以获取它的地址 + 可以对它赋值
  139.         // 右值引用,不能获取地址,只能获得值
  140.         ccc::string ret_2;
  141.         ret_2 = to_string(-6789);
  142.         cout << endl << endl;
  143.         // 左值引用,就是要深拷贝,开辟空间再释放
  144.         // 右值引用,就是把将亡值,直接拿过来使用
  145.         ccc::string str1("hello");
  146.         ccc::string str2(str1);
  147.         ccc::string str3(move(str1));
  148. }
  149. void test6()
  150. {
  151.         ccc::string ret;
  152.         ret = to_string(-6789);
  153. }
复制代码
5,完善转发

模板中的&&不代表右值引用,而是万能引用,其既能汲取左值又能汲取右值。
模板的万能引用只是提供了可以或许汲取同时汲取左值引用和右值引用的本事,
但是引用范例的唯一作用就是限定了汲取的范例,后续利用中都退化成了左值,
我们渴望可以或许在通报过程中保持它的左值大概右值的属性, 就必要用到完善转发。
std::forward 完善转发能在传参的过程中生存对象原生范例属性
  1. void Fun(int& x) { cout << "左值引用" << endl; }
  2. void Fun(const int& x) { cout << "const 左值引用" << endl; }
  3. void Fun(int&& x) { cout << "右值引用" << endl; }
  4. void Fun(const int&& x) { cout << "const 右值引用" << endl; }
  5. template<typename T>
  6. void PerfectForward(T&& t)
  7. {
  8.         Fun(std::forward<T>(t));
  9. }
  10. void test8()
  11. {
  12.         PerfectForward(10);                          // 右值
  13.         int a;
  14.         PerfectForward(a);                          // 左值
  15.         PerfectForward(std::move(a)); // 右值
  16.         const int b = 8;
  17.         PerfectForward(b);                          // const 左值
  18.         PerfectForward(std::move(b)); // const 右值
  19. }
复制代码
六、新增的类功能

1,原始默认成员函数

原来C++类中,有6个默认成员函数:
1. 构造函数
2. 析构函数
3. 拷贝构造函数
4. 拷贝赋值重载
5. 取所在重载
6. const 取所在重载
2,新增默认成员函数

而C++11 新增了两个:移动构造函数,移动赋值运算符重载
针对移动构造函数和移动赋值运算符重载有一些必要注意的点如下:
1.如果你没有本身实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的恣意一个。那么编译器会主动天生一个默认移动构造。默认天生的移动构造函数,对于内置范例成员会实行逐成员按字节拷贝,自界说范例成员,则必要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
2.如果你没有本身实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的恣意一个,那么编译器会主动天生一个默认移动赋值。默认天生的移动构造函数,对于内置范例成员会实行逐成员按字节拷贝,自界说范例成员,则必要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全雷同)
3.如果你提供了移动构造大概移动赋值,编译器不会主动提供拷贝构造和拷贝赋值。
3,关键字default

欺压天生默认函数的关键字default:
C++11可以让你更好的控制要利用的默认函数。
假设你要利用某个默认的函数,但是由于一些缘故起因这个函数没有默认天生。
比如:我们提供了拷贝构造,就不会天生移动构造了,那么我们可以利用default关键字表现指定移动构造天生
4,关键字delete

克制天生默认函数的关键字delete:
如果能想要限定某些默认函数的天生,在C++98中,是该函数设置成private,而且只声明补丁已,如许只要其他人想要调用就会报错。
在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不天生对应函数的默认版本,称=delete修饰的函数为删除函数。
  1. class Person
  2. {
  3. public:
  4.         Person(const char* name = "", int age = 0)
  5.                 :_name(name)
  6.                 , _age(age)
  7.         {}
  8.         Person(const Person& p)
  9.                 :_name(p._name)
  10.                 , _age(p._age)
  11.         {}
  12.         Person(Person&& p) = default;
  13.         //Person(const Person& p) = delete;
  14. private:
  15.         ccc::string _name;
  16.         int _age;
  17. };
  18. void test9()
  19. {
  20.         Person s1;
  21.         Person s2 = s1;
  22.         Person s3 = std::move(s1);
  23. }
复制代码
七、可变参数模板

1,语法格式

C++11的新特性可变参数模板,能创建继承可变参数的函数模板和类模板,相比 C++98/03,类模版和函数模版中只能含固定命目的模版参数,可变模版参数无疑是一个巨大的改进。
可变参数函数的语法格式
  1. // Args是一个模板参数包,args是一个函数形参参数包
  2. // 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
  3. template <class ...Args>
  4. void ShowList(Args... args)
  5. {}
复制代码
上面的参数args前面有省略号,以是它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它内里包罗了0到N(N>=0)个模版参数。
我们无法直接获取参数包args中的每个参数的,只能通过睁开参数包的方式来获取参数包中的每个参数,这是利用可变模版参数的一个重要特点,也是最大的难点,即怎样睁开可变模版参数。
由于语法不支持利用args如许方式获取可变参数,以是我们的用一些奇招来逐一获取参数包的值。
2,递归睁开

递归函数方式睁开参数包
  1. // 递归终止函数
  2. template <class T>
  3. void ShowList(const T& t)
  4. {
  5. cout << t << endl;
  6. }
  7. // 展开函数
  8. template <class T, class ...Args>
  9. void ShowList(T value, Args... args)
  10. {
  11. cout << value <<" ";
  12. ShowList(args...);
  13. }
  14. int main()
  15. {
  16. ShowList(1);
  17. ShowList(1, 'A');
  18. ShowList(1, 'A', std::string("sort"));
  19. return 0;
  20. }
复制代码
3,逗号表达式睁开

逗号表达式睁开参数包
  1. template <class T>
  2. void PrintArg(T t)
  3. {
  4. cout << t << " ";
  5. }
  6. //展开函数
  7. template <class ...Args>
  8. void ShowList(Args... args)
  9. {
  10. int arr[] = { (PrintArg(args), 0)... };
  11. cout << endl;
  12. }
  13. int main()
  14. {
  15. ShowList(1);
  16. ShowList(1, 'A');
  17. ShowList(1, 'A', std::string("sort"));
  18. return 0;
  19. }
复制代码
八、lambda表达式

1,数组元素排序

  1. void test12()
  2. {
  3.         // C++98
  4.         int array[] = { 4,1,8,5,3,7,0,9,2,6 };
  5.         for (auto e : array) { cout << e << " "; }
  6.         cout << endl;
  7.         // 默认按照小于比较,排出来结果是升序
  8.         std::sort(array, array + sizeof(array) / sizeof(array[0]));
  9.         for (auto e : array) { cout << e << " "; }
  10.         cout << endl;
  11.         // 如果需要降序,需要改变元素的比较规则
  12.         std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());
  13.         for (auto e : array) { cout << e << " "; }
  14.         cout << endl;
  15. }
复制代码
2,自界说类排序和lambda应用

  1. struct Goods
  2. {
  3.         string _name;  // 名字
  4.         double _price; // 价格
  5.         int _evaluate; // 评价
  6.         Goods(const char* str, double price, int evaluate)
  7.                 :_name(str)
  8.                 , _price(price)
  9.                 , _evaluate(evaluate)
  10.         {}
  11. };
  12. struct ComparePriceLess
  13. {
  14.         bool operator()(const Goods& gl, const Goods& gr)
  15.         {
  16.                 return gl._price < gr._price;
  17.         }
  18. };
  19. struct ComparePriceGreater
  20. {
  21.         bool operator()(const Goods& gl, const Goods& gr)
  22.         {
  23.                 return gl._price > gr._price;
  24.         }
  25. };
  26. void test13()
  27. {
  28.         //C++98排序,需要调用类成员函数
  29.         vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };
  30.         sort(v.begin(), v.end(), ComparePriceLess());
  31.         sort(v.begin(), v.end(), ComparePriceGreater());
  32.         //C++11排序,可以使用lambda表达式
  33.         vector<Goods> v2 = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
  34.         sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; });
  35.         sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price; });
  36.         sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate < g2._evaluate; });
  37.         sort(v.begin(), v2.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate; });
  38. }
复制代码
3,lambda表达式的语法格式

lambda表达式誊写格式为:
[capture-list] (parameters) mutable -> return-type { statement }
lambda表达式各部门分析:
[capture-list] : 捕获列表,该列表总是出如今lambda函数的开始位置,编译器根据[]来 判定接下来的代码是否为lambda函数,捕获列表可以或许捕获上下文中的变量供lambda 函数利用。
(parameters):参数列表。与平凡函数的参数列表同等,如果不必要参数通报,则可以 连同()一起省略 。
mutable:取消常性性,默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。利用该修饰符时,参数列表不可省略(纵然参数为空)。
->returntype:返回值范例。用追踪返回范例情势声明函数的返回值范例,没有返回值时此部门可省略。返回值范例明白情况下,也可省略,由编译器对返回范例举行推导。
{statement}:函数体。在该函数体内,除了可以利用其参数外,还可以利用全部捕获到的变量。
  1. void test14()
  2. {
  3.         // 最简单的lambda表达式, 该lambda表达式没有任何意义
  4.         [] {};
  5.         // 省略参数列表和返回值类型,返回值类型由编译器推导为int
  6.         int a = 3, b = 4;
  7.         [=] {return a + 3; };
  8.         // 省略了返回值类型,无返回值类型
  9.         auto fun1 = [&](int c) {b = a + c; };
  10.         fun1(10);
  11.         cout << a << " " << b << endl;
  12.         // 各部分都很完善的lambda函数
  13.         auto fun2 = [=, &b](int c)->int {return b += a + c; };
  14.         cout << fun2(10) << endl;
  15.         // 复制捕捉x
  16.         int x = 10;
  17.         auto add_x = [x](int a) mutable { x *= 2; return a + x; };
  18.         cout << add_x(10) << endl;
  19. }
  20. void (*PF)();
  21. void test15()
  22. {
  23.         auto f1 = [] {cout << "hello world" << endl; };
  24.         auto f2 = [] {cout << "hello world" << endl; };
  25.         //f1 = f2;   // 编译失败--->提示找不到operator=()
  26.         auto f3(f2);
  27.         f3();
  28.         // 可以将lambda表达式赋值给相同类型的函数指针
  29.         PF = f2;
  30.         PF();
  31. }
复制代码
4,捕获列表的语法格式

捕获列表形貌了上下文中那些数据可以被lambda利用,利用的方式为传值还是传引用。
[var]:体现值通报方式捕获变量var
[=]:体现值通报方式捕获全部父作用域中的变量(包罗this)
[&var]:体现引用通报捕获变量var
[&]:体现引用通报捕获全部父作用域中的变量(包罗this)
[this]:体现值通报方式捕获当前的this指针
注意:
a. 父作用域指包罗lambda函数的语句块
b. 语法上捕获列表可由多个捕获项构成,并以逗号分割
比如:[=, &a, &b]: 以引用通报的方式捕获a和b,值通报的方式捕获其他全部变量
c. 捕获列表不允许变量重复通报,否则就会导致编译错误
比如:[=,a]: 已经以传值的方式捕获了全部变量,捕获a重复会导致报错
d. 在块作用域以外的lambda函数捕获列表必须为空
e. lambda表达式之间不能相互赋值,纵然看起来范例雷同
  1. void test16()
  2. {
  3.         int a, b, c, d, e;
  4.         a = b = c = d = e = 1;
  5.         // 传值捕捉
  6.         auto f1 = [=]()
  7.         {
  8.                 cout << a << b << c << d << e << endl;
  9.         };
  10.         f1();
  11.         // 引用捕捉
  12.         auto f2 = [&]()
  13.         {
  14.                 cout << a << b << c << d << e << endl;
  15.         };
  16.         f2();
  17.         // 混合捕捉
  18.         auto f3 = [=, &a]() //以引用传递的方式捕捉变量a,值传递方式捕捉其他所有变量
  19.         {
  20.                 a++;
  21.                 cout << a << b << c << d << e << endl;
  22.         };
  23.         f3();
  24.         // 混合捕捉
  25.         auto f4 = [&, a]() //值传递方式捕捉变量a,引用方式捕捉其他变量
  26.         {
  27.                 //a++; 值传递捕捉,无法修改变量值
  28.                 b++;
  29.                 c++;
  30.                 d++;
  31.                 e++;
  32.                 cout << a << b << c << d << e << endl;
  33.         };
  34.         f4();
  35. }
复制代码
九、包装器

1,包装器的利用场景

function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。 那么我们来看看,我们为什么必要function呢?
由于function可以镌汰函数模板实例化。包装前,useF函数模板实例化了三份;包装后,useF函数模板实例化了一份。
  1. double func(double d)
  2. {
  3.         return d / 2;
  4. }
  5. int add(int a, int b)
  6. {
  7.         return a + b;
  8. }
  9. struct Functor
  10. {
  11.         double operator()(double d)
  12.         {
  13.                 return d / 3;
  14.         }
  15.         int operator() (int a, int b)
  16.         {
  17.                 return a + b;
  18.         }
  19. };
  20. class Plus
  21. {
  22. public:
  23.         static int plusi(int a, int b)
  24.         {
  25.                 return a + b;
  26.         }
  27.         double plusd(double a, double b)
  28.         {
  29.                 return a + b;
  30.         }
  31. };
  32. void test17()
  33. {
  34.         //包装前,实例化3次
  35.         // 函数名
  36.         cout << useF(func, 11.11) << endl;
  37.         // 函数对象(仿函数)
  38.         cout << useF(Functor(), 11.11) << endl;
  39.         // lamber表达式
  40.         cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
  41.         //包装后,只实例化1次
  42.   // 函数名
  43.         function<double(double)> f1 = func;
  44.         cout << useF(f1, 11.11) << endl;
  45.         // 函数对象(仿函数)
  46.         function<double(double)> f2 = Functor();
  47.         cout << useF(f2, 11.11) << endl;
  48.   // lamber表达式
  49.         function<double(double)> f3 = [](double d)->double { return d / 4; };
  50.         cout << useF(f3, 11.11) << endl;
  51. }
  52. void test18()
  53. {
  54.         function<int(int, int)> f1 = add;
  55.         f1(1, 2);
  56.         function<int(int, int)> f2 = Functor();
  57.         f2(1, 2);
  58.         function<int(int, int)> f3 = Plus::plusi;
  59.         f3(1, 2);
  60.         function<double(Plus, double, double)> f4 = &Plus::plusd;
  61.         f4(Plus(), 1.1, 2.2);
  62. }
复制代码
2,包装器的语法格式

  1. #include <functional>
  2. // 类模板原型如下
  3. template <class T> function;   // undefined
  4. template <class Ret, class... Args>
  5. class function<Ret(Args...)>;
  6. 模板参数说明:
  7. Ret: 被调用函数的返回类型
  8. Args…:被调用函数的形参
复制代码
3,bind函数

std::bind函数界说在头文件中,是一个函数模板,它就像一个函数包装器(适配器),继承一个可调用对象(callable object),天生一个新的可调用对象来“顺应”原对象的参数列表。
一样寻常而言,我们用它可以把一个原来汲取N个参数的函数fn,通过绑定一些参数,返回一个汲取M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,利用std::bind函数还可以实现参数次序调解等操纵。
语法原型如下:
  1. // 原型如下:
  2. template <class Fn, class... Args>
  3. /* unspecified */ bind (Fn&& fn, Args&&... args);
  4. // with return type (2)
  5. template <class Ret, class Fn, class... Args>
  6. /* unspecified */ bind (Fn&& fn, Args&&... args);
复制代码
可以将bind函数看作是一个通用的函数适配器,它继承一个可调用对象,天生一个新的可调用对象来“顺应”原对象的参数列表。
调用bind的一样寻常情势:auto newCallable = bind(callable,arg_list);
newCallable:本身是一个可调用对象,
arg_list:是一个逗号分隔的参数列表,
callable:是对应的给定参数。
当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。
arg_list中的参数大概包罗形如_n的名字,此中n是一个整数,这些参数是“占位符”,体现 newCallable的参数,它们占据了通报给newCallable的参数的“位置”。数值n体现天生的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。
  1. int Div(int a, int b)
  2. {
  3.         return a / b;
  4. }
  5. int Add(int a, int b)
  6. {
  7.         return a + b;
  8. }
  9. int Mul(int a, int b, int rate)
  10. {
  11.         return a * b * rate;
  12. }
  13. class Sub
  14. {
  15. public:
  16.         int sub(int a, int b)
  17.         {
  18.                 return a - b;
  19.         }
  20. };
  21. void test19()
  22. {
  23.         int x = 2;
  24.         int y = 10;
  25.         cout << Div(x, y) << endl;
  26.         // 调整顺序
  27.         auto bindFunc1 = bind(Div, placeholders::_1, placeholders::_2);
  28.         cout << bindFunc1(x, y) << endl;
  29.         function<int(int, int)> bindFunc2 = bind(Div, placeholders::_2, placeholders::_1);
  30.         cout << bindFunc2(x, y) << endl;
  31.         //调整个数,绑定死固定参数
  32.         function<int(int, int)> funcAdd = Add;
  33.         function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);
  34.         function<int(int, int)> funcMul = bind(Mul, placeholders::_1, placeholders::_2, 6);
  35.         map<string, function<int(int, int)>> ofFuncMap =
  36.         {
  37.                 {"+", Add},
  38.                 {"-", bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2)},
  39.         };
  40.         cout << funcAdd(1, 2) << endl;
  41.         cout << funcSub(1, 2) << endl;
  42.         cout << funcMul(2, 1) << endl;
  43.         cout << ofFuncMap["+"](1, 2) << endl;
  44.         cout << ofFuncMap["-"](1, 2) << endl;
  45. }
复制代码
点个赞吧!666

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表