C++--C++11(下)

打印 上一主题 下一主题

主题 1716|帖子 1716|积分 5148


目录
7.5 完善转发
8 新的类功能
9 可变参数模板
10 lambda表达式
11 包装器


7.5 完善转发

   模板中的  &&   万能引用   
  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. // 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
  6. // 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
  7. // 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
  8. // 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发
  9. template<typename T>
  10. void PerfectForward(T&& t)
  11. {
  12. Fun(t);
  13. }
  14. int main()
  15. {
  16. PerfectForward(10);           // 右值
  17. int a;
  18. PerfectForward(a);            // 左值
  19. PerfectForward(std::move(a)); // 右值
  20. const int b = 8;
  21. PerfectForward(b);      // const 左值
  22. PerfectForward(std::move(b)); // const 右值
  23. return 0;
  24. }
复制代码
    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. // std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
  6. template<typename T>
  7. void PerfectForward(T&& t)
  8. {
  9. Fun(std::forward<T>(t));
  10. }
  11. int main()
  12. {
  13. PerfectForward(10);           // 右值
  14. int a;
  15. PerfectForward(a);            // 左值
  16. PerfectForward(std::move(a)); // 右值
  17. const int b = 8;
  18. PerfectForward(b);      // const 左值
  19. PerfectForward(std::move(b)); // const 右值
  20. return 0;
  21. }
复制代码
      完善转发实际中的利用场景:          
  1. template<class T>
  2. struct ListNode
  3. {
  4. ListNode* _next = nullptr;
  5. ListNode* _prev = nullptr;
  6. T _data;
  7. };
  8. template<class T>
  9. class List
  10. {
  11. typedef ListNode<T> Node;
  12. public:
  13. List()
  14. {
  15. _head = new Node;
  16. _head->_next = _head;
  17. _head->_prev = _head;
  18. }
  19. void PushBack(T&& x)
  20. {
  21. //Insert(_head, x);
  22. Insert(_head, std::forward<T>(x));
  23. }
  24. void PushFront(T&& x)
  25. {
  26. //Insert(_head->_next, x);
  27. Insert(_head->_next, std::forward<T>(x));
  28. }
  29. void Insert(Node* pos, T&& x)
  30. {
  31. Node* prev = pos->_prev;
  32. Node* newnode = new Node;
  33. newnode->_data = std::forward<T>(x); // 关键位置
  34. // prev newnode pos
  35. prev->_next = newnode;
  36. newnode->_prev = prev;
  37. newnode->_next = pos;
  38. pos->_prev = newnode;
  39. }
  40. void Insert(Node* pos, const T& x)
  41. {
  42. Node* prev = pos->_prev;
  43. Node* newnode = new Node;
  44. newnode->_data = x; // 关键位置
  45. // prev newnode pos
  46. prev->_next = newnode;
  47. newnode->_prev = prev;
  48. newnode->_next = pos;
  49. pos->_prev = newnode;
  50. }
  51. private:
  52. Node* _head;
  53. };
  54. int main()
  55. {
  56. List<bit::string> lt;
  57. lt.PushBack("1111");
  58. lt.PushFront("2222");
  59. return 0;
  60. }
复制代码
   8 新的类功能

         默认成员函数              原来     C++     类中,有     6     个默认成员函数:              1.      构造函数              2.      析构函数              3.      拷贝构造函数              4.      拷贝赋值重载              5.      取所在重载              6. const      取所在重载              最后紧张的是前     4     个,后两个用处不大。默认成员函数就是我们不写编译器会天生一个默认的。              C++11      新增了两个:移动构造函数和移动赋值运算符重载。              针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:              假如你没有本身实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任              意一个。那么编译器会主动天生一个默认移动构造。默认天生的移动构造函数,对于内置类              型成员会执行逐成员按字节拷贝,自界说范例成员,则需要看这个成员是否实现移动构造,              假如实现了就调用移动构造,没有实现就调用拷贝构造。              假如你没有本身实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中              的任意一个,那么编译器会主动天生一个默认移动赋值。默认天生的移动构造函数,对于内              置范例成员会执行逐成员按字节拷贝,自界说范例成员,则需要看这个成员是否实现移动赋              值,假如实现了就调用移动赋值,没有实现就调用拷贝赋值。     (     默认移动赋值跟上面移动构造              完全类似     )              假如你提供了移动构造或者移动赋值,编译器不会主动提供拷贝构造和拷贝赋值。            
  1. // 以下代码在vs2013中不能体现,在vs2019下才能演示体现上面的特性。
  2. class Person
  3. {
  4. public:
  5. Person(const char* name = "", int age = 0)
  6. :_name(name)
  7. , _age(age)
  8. {}
  9. /*Person(const Person& p)
  10. :_name(p._name)
  11. ,_age(p._age)
  12. {}*/
  13. /*Person& operator=(const Person& p)
  14. {
  15. if(this != &p)
  16. {
  17. _name = p._name;
  18. _age = p._age;
  19. }
  20. return *this;
  21. }*/
  22. /*~Person()
  23. {}*/
  24. private:
  25. bit::string _name;
  26. int _age;
  27. };
  28. int main()
  29. {
  30. Person s1;
  31. Person s2 = s1;
  32. Person s3 = std::move(s1);
  33. Person s4;
  34. s4 = std::move(s2);
  35. return 0;
  36. }
复制代码
          类成员变量初始化                 C++11      允许在类界说时给成员变量初始缺省值,默认天生构造函数会利用这些缺省值初始化,这                 个我们在雷和对象默认就讲了,这里就不再细讲了。                 逼迫天生默认函数的关键字      default:                 C++11      可以让你更好的控制要利用的默认函数。假设你要利用某个默认的函数,但是因为一些原                 因这个函数没有默认天生。比如:我们提供了拷贝构造,就不会天生移动构造了,那么我们可以                 利用      default      关键字显示指定移动构造天生。               
  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. private:
  14. bit::string _name;
  15. int _age;
  16. };
  17. int main()
  18. {
  19. Person s1;
  20. Person s2 = s1;
  21. Person s3 = std::move(s1);
  22. return 0;
  23. }
复制代码
            禁止天生默认函数的关键字       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) = delete;
  9. private:
  10. bit::string _name;
  11. int _age;
  12. };
  13. int main()
  14. {
  15. Person s1;
  16. Person s2 = s1;
  17. Person s3 = std::move(s1);
  18. return 0;
  19. }
复制代码
      9 可变参数模板

               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         这样方式获取可变                          参数,所以我们的用一些奇招来一一获取参数包的值。                                   递归函数方式展开参数包          
  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. }
复制代码
                    STL           容器中的           empalce           相干接口函数:                                http://www.cplusplus.com/reference/vector/vector/emplace_back/                                http://www.cplusplus.com/reference/list/list/emplace_back/                                            起首我们看到的            emplace            系列的接口,支持模板的可变参数,并且万能引用。那么相对            insert            和                                   emplace            系列接口的上风到底在哪里呢?                                 
  1. int main()
  2. {
  3. std::list< std::pair<int, char> > mylist;
  4. // emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象
  5. // 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别
  6. mylist.emplace_back(10, 'a');
  7. mylist.emplace_back(20, 'b');
  8. mylist.emplace_back(make_pair(30, 'c'));
  9. mylist.push_back(make_pair(40, 'd'));
  10. mylist.push_back({ 50, 'e' });
  11. for (auto e : mylist)
  12. cout << e.first << ":" << e.second << endl;
  13. return 0;
  14. }
复制代码
         
  1. int main()
  2. {
  3. // 下面我们试一下带有拷贝构造和移动构造的bit::string,再试试呢
  4. // 我们会发现其实差别也不到,emplace_back是直接构造了,push_back
  5. // 是先构造,再移动构造,其实也还好。
  6. std::list< std::pair<int, bit::string> > mylist;
  7. mylist.emplace_back(10, "sort");
  8. mylist.emplace_back(make_pair(20, "sort"));
  9. mylist.push_back(make_pair(30, "sort"));
  10. mylist.push_back({ 40, "sort"});
  11. return 0;
  12. }
复制代码
           10 lambda表达式

                         10.1 C++98             中的一个例子                                      在             C++98             中,假如想要对一个数据集合中的元素进行排序,可以利用             std::sort             方法。                                    
  1. #include <algorithm>
  2. #include <functional>
  3. int main()
  4. {
  5. int array[] = {4,1,8,5,3,7,0,9,2,6};
  6. // 默认按照小于比较,排出来结果是升序
  7. std::sort(array, array+sizeof(array)/sizeof(array[0]));
  8. // 如果需要降序,需要改变元素的比较规则
  9. std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());
  10. return 0;
  11. }
复制代码
                          假如待排序元素为自界说范例,需要用户界说排序时的比较规则:                                       
  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. int main()
  27. {
  28. vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
  29. 3 }, { "菠萝", 1.5, 4 } };
  30. sort(v.begin(), v.end(), ComparePriceLess());
  31. sort(v.begin(), v.end(), ComparePriceGreater());
  32. }
复制代码
                            随着               C++               语法的发展,               人们开始觉得上面的写法太复杂了,每次为了实现一个               algorithm               算法,                                            都要重新去写一个类,假如每次比较的逻辑不一样,还要去实现多个类,特殊是相同类的定名,                                            这些都给编程者带来了极大的未便               。因此,在               C++11               语法中出现了               Lambda               表达式。                                                           10.2 lambda                表达式                                              
  1. int main()
  2. {
  3. vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
  4. 3 }, { "菠萝", 1.5, 4 } };
  5. sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
  6. return g1._price < g2._price; });
  7. sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
  8. return g1._price > g2._price; });
  9. sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
  10. return g1._evaluate < g2._evaluate; });
  11. sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){
  12. return g1._evaluate > g2._evaluate; });
  13. }
复制代码
                                上述代码就是利用                 C++11                 中的                 lambda                 表达式来解决,可以看出                 lambda                 表达式实际是一个匿名函                                                  数。                                                  10.3 lambda                 表达式语法                                                  lambda                 表达式誊写格式:                 [capture-list] (parameters) mutable -> return-type { statement                                                  }                                                  1. lambda                 表达式各部分说明                                                  [capture-list] :                  捕捉列表                 ,该列表总是出如今                 lambda                 函数的开始位置,                 编译器根据                 []                                                                  判定接下来的代码是否为                 lambda                 函数                 ,                 捕捉列表可以或许捕捉上下文中的变量供                 lambda                                                  函数利用                 。                                                  (parameters)                 :参数列表。与                 普通函数的参数列表划一                 ,假如不需要参数传递,则可以                                                  连同                 ()                 一起省略                                                  mutable                 :默认情况下,                 lambda                 函数总是一个                 const                 函数,                 mutable                 可以取消其常量                                                  性。利用该修饰符时,参数列表不可省略                 (                 纵然参数为空                 )                 。                                                                    ->returntype                  :返回值范例                  。用                  追踪返回范例情势声明函数的返回值范例                  ,没有返回                                                     值时此部分可省略。                  返回值范例明确情况下,也可省略,由编译器对返回范例进行推                                                                       。                                                     {statement}                  :函数体                  。在该函数体内,除了可以利用其参数外,还可以利用所有捕获                                                     到的变量。                                                     注意:                                                     在                  lambda                  函数界说中,                  参数列表和返回值范例都是可选部分,而捕捉列表和函数体可以为                                                                       。因此                  C++11                  中                  最简朴的                  lambda                  函数为:                  []{}                  ;                   该                  lambda                  函数不能做任何事情。                                                   
  1. int main()
  2. {
  3.    // 最简单的lambda表达式, 该lambda表达式没有任何意义
  4.   []{};
  5.    
  6.    // 省略参数列表和返回值类型,返回值类型由编译器推导为int
  7.    int a = 3, b = 4;
  8.   [=]{return a + 3; };
  9.    
  10.    // 省略了返回值类型,无返回值类型
  11.    auto fun1 = [&](int c){b = a + c; };
  12.    fun1(10)
  13.    cout<<a<<" "<<b<<endl;
  14.    
  15.    // 各部分都很完善的lambda函数
  16.    auto fun2 = [=, &b](int c)->int{return b += a+ c; };
  17.    cout<<fun2(10)<<endl;
  18.    
  19.    // 复制捕捉x
  20.    int x = 10;
  21.    auto add_x = [x](int a) mutable { x *= 2; return a + x; };
  22.    cout << add_x(10) << endl;
  23.    return 0;
  24. }
复制代码
                                    通过上述例子可以看出,                   lambda                   表达式实际上可以理解为无名函数,该函数无法直接调                                                        用,假如想要直接调用,可借助                   auto                   将其赋值给一个变量。                                                        2.                    捕获列表说明                                                        捕捉列表描述了上下文中那些数据可以被                   lambda                   利用                   ,以及                   利用的方式传值照旧传引用                   。                                                        [var]                   :表示值传递方式捕捉变量                   var                                                        [=]                   :表示值传递方式捕获所有父作用域中的变量                   (                   包罗                   this)                                                        [&var]                   :表示引用传递捕捉变量                   var                                                        [&]                   :表示引用传递捕捉所有父作用域中的变量                   (                   包罗                   this)                                                        [this]                   :表示值传递方式捕捉当前的                   this                   指针                                                        注意:                                                        a.                    父作用域指包含                   lambda                   函数的语句块                                                        b.                    语法上捕捉列表可由多个捕捉项组成,并以逗号分割                   。                                                                          比如:                   [=, &a, &b]                   :以引用传递的方式捕捉变量                   a                   和                   b                   ,值传递方式捕捉其他所有变量                                                        [&                   ,                   a, this]                   :值传递方式捕捉变量                   a                   和                   this                   ,引用方式捕捉其他变量                                                        c.                    捕捉列表不允许变量重复传递,否则就会导致编译错误                   。                                                                          比如:                   [=, a]                   :                   =                   已经以值传递方式捕捉了所有变量,捕捉                   a                   重复                                                                            d.                     在块作用域以外的                    lambda                    函数捕捉列表必须为空                    。                                                           e.                     在块作用域中的                    lambda                    函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者                                                           非局部变量都                                                                             会导致编译报错。                                                           f.                     lambda                    表达式之间不能相互赋值                    ,纵然看起来范例相同                                                         
  1. void (*PF)();
  2. int main()
  3. {
  4. auto f1 = []{cout << "hello world" << endl; };
  5. auto f2 = []{cout << "hello world" << endl; };
  6.    // 此处先不解释原因,等lambda表达式底层实现原理看完后,大家就清楚了
  7. //f1 = f2;   // 编译失败--->提示找不到operator=()
  8.    // 允许使用一个lambda表达式拷贝构造一个新的副本
  9. auto f3(f2);
  10. f3();
  11. // 可以将lambda表达式赋值给相同类型的函数指针
  12. PF = f2;
  13. PF();
  14. return 0;
  15. }
  16. class Rate
  17. {
  18. public:
  19. Rate(double rate): _rate(rate)
  20. {}
  21. double operator()(double money, int year)
  22. { return money * _rate * year;}
  23. private:
  24. double _rate;
  25. };
  26. int main()
  27. {
  28. // 函数对象
  29. double rate = 0.49;
  30. Rate r1(rate);
  31. r1(10000, 2);
  32. // lamber
  33. auto r2 = [=](double monty, int year)->double{return monty*rate*year;
  34. };
  35. r2(10000, 2);
  36. return 0;
复制代码
                                        10.4                      函数对象与                     lambda                     表达式                                                              函数对象,又称为仿函数,即可以想函数一样利用的对象,就是在类中重载了                     operator()                     运算符的                                                              类对象。                                                            
  1. class Rate
  2. {
  3. public:
  4. Rate(double rate): _rate(rate)
  5. {}
  6. double operator()(double money, int year)
  7. { return money * _rate * year;}
  8. private:
  9. double _rate;
  10. };
  11. int main()
  12. {
  13. // 函数对象
  14. double rate = 0.49;
  15. Rate r1(rate);
  16. r1(10000, 2);
  17. // lamber
  18. auto r2 = [=](double monty, int year)->double{return monty*rate*year;
  19. };
  20. r2(10000, 2);
  21. return 0;
  22. }
复制代码
                                          从利用方式上来看,函数对象与                      lambda                      表达式完全一样。                                                                 函数对象将                      rate                      作为其成员变量,在界说对象时给出初始值即可,                      lambda                      表达式通过捕获列表可                                                                 以直接将该变量捕获到。                                                                 
                                                                                                                                                                                                                                         
   实际在底层编译器对于  lambda  表达式的处理方式,完全就是按照函数对象的方式处理的,即:如     果界说了一个  lambda  表达式,编译器会主动天生一个类,在该类中重载了  operator()  。   
11 包装器

   function  包装器     function  包装器 也叫作适配器。  C++  中的  function  本质是一个类模板,也是一个包装器。     那么我们来看看,我们为什么需要  function  呢?   
  1. ret = func(x);
  2. // 上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能
  3. 是lamber表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!
  4. 为什么呢?我们继续往下看
  5. template<class F, class T>
  6. T useF(F f, T x)
  7. {
  8. static int count = 0;
  9. cout << "count:" << ++count << endl;
  10. cout << "count:" << &count << endl;
  11. return f(x);
  12. }
  13. double f(double i)
  14. {
  15. return i / 2;
  16. }
  17. struct Functor
  18. {
  19. double operator()(double d)
  20. {
  21. return d / 3;
  22. }
  23. };
  24. int main()
  25. {
  26. // 函数名
  27. cout << useF(f, 11.11) << endl;
  28. // 函数对象
  29. cout << useF(Functor(), 11.11) << endl;
  30. // lamber表达式
  31. cout << useF([](double d)->double{ return d/4; }, 11.11) << endl;
  32. return 0;
  33. }
复制代码
    通过上面的步伐验证,我们会发现   useF   函数模板实例化了三份。        包装器可以很好的解决上面的问题      
  1. std::function在头文件<functional>
  2. // 类模板原型如下
  3. template <class T> function;     // undefined
  4. template <class Ret, class... Args>
  5. class function<Ret(Args...)>;
  6. 模板参数说明:
  7. Ret: 被调用函数的返回类型
  8. Args…:被调用函数的形参
  9. // 使用方法如下:
  10. #include <functional>
  11. int f(int a, int b)
  12. {
  13. return a + b;
  14. }
  15. struct Functor
  16. {
  17. public:
  18. int operator() (int a, int b)
  19. {
  20. return a + b;
  21. }
  22. };
  23. class Plus
  24. {
  25. public:
  26. static int plusi(int a, int b)
  27. {
  28. return a + b;
  29. }
  30. double plusd(double a, double b)
  31. {
  32. return a + b;
  33. }
  34. };
  35. int main()
  36. {
  37. // 函数名(函数指针)
  38. std::function<int(int, int)> func1 = f;
  39. cout << func1(1, 2) << endl;
  40. // 函数对象
  41. std::function<int(int, int)> func2 = Functor();
  42. cout << func2(1, 2) << endl;
  43. // lamber表达式
  44. std::function<int(int, int)> func3 = [](const int a, const int b)
  45. {return a + b; };
  46. cout << func3(1, 2) << endl;
  47. // 类的成员函数
  48. std::function<int(int, int)> func4 = &Plus::plusi;
  49. cout << func4(1, 2) << endl;
  50. std::function<double(Plus, double, double)> func5 = &Plus::plusd;
  51. cout << func5(Plus(), 1.1, 2.2) << endl;
  52. return 0;
  53. }
复制代码
      有了包装器,如何解决模板的效率低下,实例化多份的问题呢?         
  1. #include <functional>
  2. template<class F, class T>
  3. T useF(F f, T x)
  4. {
  5. static int count = 0;
  6. cout << "count:" << ++count << endl;
  7. cout << "count:" << &count << endl;
  8. return f(x);
  9. }
  10. double f(double i)
  11. {
  12. return i / 2;
  13. }
  14. struct Functor
  15. {
  16. double operator()(double d)
  17. {
  18. return d / 3;
  19. }
  20. };
  21. int main()
  22. {
  23. // 函数名
  24. std::function<double(double)> func1 = f;
  25. cout << useF(func1, 11.11) << endl;
  26. // 函数对象
  27. std::function<double(double)> func2 = Functor();
  28. cout << useF(func2, 11.11) << endl;
  29. // lamber表达式
  30. std::function<double(double)> func3 = [](double d)->double{ return d /
  31. 4; };
  32. cout << useF(func3, 11.11) << endl;
  33. return 0;
  34. }
复制代码
        包装器的其他一些场景:              https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/submissions/            
  1. class Solution {
  2. public:
  3. int evalRPN(vector<string>& tokens) {
  4.  stack<int> st;
  5.     for(auto& str : tokens)
  6.     {
  7.         if(str == "+" || str == "-" || str == "*" || str == "/")
  8.         {
  9.             int right = st.top();
  10.             st.pop();
  11.             int left = st.top();
  12.             st.pop();
  13.             switch(str[0])
  14.         {
  15.                 case '+':
  16.                     st.push(left+right);
  17.                     break;
  18.                 case '-':
  19.                     st.push(left-right);
  20.                     break;
  21.                 case '*':
  22.                     st.push(left*right);
  23.                     break;
  24.                 case '/':
  25.                     st.push(left/right);
  26.                     break;
  27.             }
  28.         }
  29.         else
  30.         {
  31.             // 1、atoi itoa
  32.             // 2、sprintf scanf
  33.             // 3、stoi to_string C++11
  34.             st.push(stoi(str));
  35.         }
  36.     }
  37.     return st.top();
  38. }
  39. };
  40. // 使用包装器以后的玩法
  41. class Solution {
  42. public:
  43. int evalRPN(vector<string>& tokens) {
  44.  stack<int> st;
  45.     map<string, function<int(int, int)>> opFuncMap =
  46. {
  47. { "+", [](int i, int j){return i + j; } },
  48. { "-", [](int i, int j){return i - j; } },
  49. { "*", [](int i, int j){return i * j; } },
  50. { "/", [](int i, int j){return i / j; } }
  51. };
  52.  for(auto& str : tokens)
  53. {
  54.         if(opFuncMap.find(str) != opFuncMap.end())
  55.         {
  56.             int right = st.top();
  57.             st.pop();
  58.             int left = st.top();
  59.             st.pop();
  60.             st.push(opFuncMap[str](left, right));
  61.     }
  62.         else
  63.         {
  64.             // 1、atoi itoa
  65.             // 2、sprintf scanf
  66.             // 3、stoi to_string C++11
  67.             st.push(stoi(str));
  68.         }
  69.     }
  70.     return st.top();
  71. }
  72. };
复制代码
          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. // 使用举例
  2. #include <functional>
  3. int Plus(int a, int b)
  4. {
  5. return a + b;
  6. }
  7. class Sub
  8. {
  9. public:
  10. int sub(int a, int b)
  11. {
  12. return a - b;
  13. }
  14. };
  15. int main()
  16. {
  17. //表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
  18. std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1,
  19. placeholders::_2);
  20. //auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
  21. //func2的类型为 function<void(int, int, int)> 与func1类型一样
  22. //表示绑定函数 plus 的第一,二为: 1, 2
  23. auto  func2 = std::bind(Plus, 1, 2);  
  24. cout << func1(1, 2) << endl;
  25. cout << func2() << endl;
  26. Sub s;
  27. // 绑定成员函数
  28. std::function<int(int, int)> func3 = std::bind(&Sub::sub, s,
  29. placeholders::_1, placeholders::_2);
  30. // 参数调换顺序
  31. std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,
  32. placeholders::_2, placeholders::_1);
  33. cout << func3(1, 2) << endl;
  34. cout << func4(1, 2) << endl;
  35. return 0;
  36. }
复制代码
      

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

伤心客

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表