STL容器中的empalce相关接口函数
emplace_back是可以不传参的,那么默认用户的就是匿名构造,传入的值就是0。
那么emplace_back的意义在哪里呢?
#include<iostream> #include<vector> using namespace std; int main() { vector<pair<int, string>>arr; arr.emplace_back(1, "xxx");//这里可以这样初始化(直接构造),push_back只能构造一个对象去传,当然emplace_back也可以构造一个对象传 return 0; }
其实就是一种优化,如果传入的是右值编译器可以直接优化成直接构造(移动构造或者是移动赋值都省下了),不需要任何拷贝构造或者是移动构造(如果是左值还是构造+深拷贝)。
lambda表达式
为什么要有lambda表达式
这个和仿函数有些类似。
举个例子,如果定义水果类,创建多个水果对象,那么他们分别有名字,价格,评价等等属性,如果想通过sort函数来实现对于不同对象的排序就要写很多个仿函数,非常的麻烦。
#include<iostream> #include<vector> #include <algorithm> #include <functional> using namespace std; struct Goods { string _name;// 名字 double _price;// 价格 int _evaluate;// 评价 Goods(const char* str, double price, int evaluate) :_name(str) , _price(price) , _evaluate(evaluate) {} }; struct ComparePriceLess { bool operator()(const Goods& gl, const Goods& gr) { return gl._price < gr._price; } }; struct ComparePriceGreater { bool operator()(const Goods& gl, const Goods& gr) { return gl._price > gr._price; } }; int main() { vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } }; sort(v.begin(), v.end(), ComparePriceLess()); sort(v.begin(), v.end(), ComparePriceGreater()); return 0; }
那么这个时候lambda表达式就可以上场了。
#include<iostream> #include<vector> #include <algorithm> #include <functional> using namespace std; struct Goods { string _name;// 名字 double _price;// 价格 int _evaluate;// 评价 Goods(const char* str, double price, int evaluate) :_name(str) , _price(price) , _evaluate(evaluate) {} }; struct ComparePriceLess { bool operator()(const Goods& gl, const Goods& gr) { return gl._price < gr._price; } }; struct ComparePriceGreater { bool operator()(const Goods& gl, const Goods& gr) { return gl._price > gr._price; } }; int main() { vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } }; sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._price < g2._price; });//按照价格排序 sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { return g1._evaluate < g2._evaluate; });//按照评价排序 return 0; }
这是按照价格排序的结果:
这是按照评价排序:
lambda表达式的格式
lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }
lambda表达式各部分说明
[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。
(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。
mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
注意:
在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为
空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。
#include<iostream> using namespace std; int main() { //其实lambda就是要给可调用的对象 auto compare = [](int x, int y) {return x > y; };//这个类型编译器认识,但是我们不认识 cout << compare(1, 2) << endl; return 0; }
那么,在外部定义的变量能在lambda表达式中使用吗?
#include<iostream> using namespace std; int main() { int a = 10; int b = 20; auto add1 = []() { return a + b; }; return 0; }
这里是不可以的,因为是两个不同的作用域,这个时候需要捕捉这两个变量才可以使用。
auto add1 = [a, b]() { return a + b; };
这个时候编译就通过了。
那么如果想交换两个变量呢?
#include<iostream> using namespace std; int main() { int a = 10; int b = 20; auto swap1 = [a, b]() { int c = a; a = b; b = c; }; return 0; }
这里是不允许的,如果想修改要加mutable。
auto swap1 = [a, b]()mutable { int c = a; a = b; b = c; };
但是外部的a和b并没有发生改变,也就是说捕捉的对象是传值拷贝,加了一个const的变量,mutable只是让他们变成非const属性的值。
如果想改变就要这样:
#include<iostream> using namespace std; int main() { int a = 10; int b = 20; auto swap1 = [&a, &b]()//这里虽然是外部a和b的别名,但是不能修改a和b的名字,不然就不能捕捉了 { int c = a; a = b; b = c; }; return 0; }
这里也无法在捕捉列表取地址。
在捕捉列表里面可以用=就是捕捉父作用域向上的变量,&是捕捉父作用域向上的变量别名。(向上就是在捕捉列表语句前面的所有变量)
#include<iostream> using namespace std; int main() { int a = 10; int b = 20; auto add1 = [=]() { return a + b; }; auto swap1 = [&]() { int c = a; a = b; b = c; }; return 0; }
注意:这些可以进行混合捕捉,比如对父作用域向上的变量进行传值捕捉,对于某一个或者是某些进行引用捕捉。
lambda的底层
#include<iostream> using namespace std; class Rate { public: Rate(double rate) : _rate(rate) {} double operator()(double money, int year) { return money * _rate * year; } private: double _rate; }; int main() { // 函数对象 double rate = 0.49; Rate r1(rate); r1(10000, 2); // lamber auto r2 = [=](double monty, int year)->double {return monty * rate * year;}; r2(10000, 2); return 0; }
第一个是仿函数的反汇编,第二个是lambda表达式的反汇编,也就是说本质都是一样的调用仿函数。
并且lambda表达式的类型名字也很繁琐。
包装器
function包装器
function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
#include<iostream> using namespace std; template<class F, class T> T useF(F f, T x) { static int count = 0; cout << "count:" << ++count << endl; cout << "count:" << &count << endl; return f(x); } double f(double i) { return i / 2; } struct Functor { double operator()(double d) { return d / 3; } }; int main() { // 函数名 cout << useF(f, 11.11) << endl; // 函数对象 cout << useF(Functor(), 11.11) << endl; // lamber表达式 cout << useF([](double d)->double { return d / 4; }, 11.11) << endl; return 0; }
这里实例化的三分不同的函数,有没有什么办法让他不生成这么多的函数。
#include<iostream> #include<functional> using namespace std; int f(int a, int b) { return a + b; } struct Functor { public: int operator() (int a, int b) { return a + b; } }; int main() { function<int(int, int)> f1;//第一个int是返回值,括号里面的是参数 f1 = f;//封装到f1中 cout << f1(1, 2) << endl; function<int(int, int)> f2(f);//这种方法也可以将f封装到f2中 cout << f2(1, 2) << endl; function<int(int, int)> f3 = Functor(); cout << f3(1, 2) << endl; /*function<int(int, int)> f4(Functor());//这里不可以,因为编译器会识别成为函数指针 cout << f4(1, 2) << endl;*/ function<int(int, int)> f5 = [](const int a, const int b){return a + b; }; cout << f5(1, 2) << endl; }
类中的成员函数也是可以包装的,但是要注意:
#include<iostream> #include<functional> using namespace std; class Plus { public: static int plusi(int a, int b) { return a + b; } int plusd(int a, int b) { return a + b; } }; int main() { //这里存入的是函数指针 function<int(int, int)> f1 = &Plus::plusi;//静态成员函数可以不加& cout << f1(1, 2) << endl; function<int(Plus, int, int)> f2 = &Plus::plusd;//非静态成员函数必须加&,这里还要加一个参数,因为传参还有一个this指针 cout << f2(Plus(), 1, 2) << endl;//这里是传进去一个Plus类型的对象,其实就是利用这个对象调用该成员函数而已 }
其实包装器就是对于可调用对象类型的大统一。
那么使用的场景呢?
class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> st; map<string, function<int(int, int)>> opMap= { {"+",[](int x, int y){return x+y;}}, {"-",[](int x, int y){return x-y;}}, {"/",[](int x, int y){return x/y;}}, {"*",[](int x, int y){return x*y;}} };//这里就是统一了类型 for(auto& e:tokens)//在map中查找符号 { if(opMap.count(e)) { int a = st.top(); st.pop(); int b = st.top(); st.pop(); st.push(opMap[e](b, a));//找到之后就将栈中的两个值通过map中储存的包装器中的lamber表达式进行运算,这里要注意数的顺序,先去取出来的在左边,后取出来的在右边 } else { st.push(stoi(e)); } } return st.top(); } };
bind
std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。
// 原型如下: template <class Fn, class... Args> /* unspecified */ bind(Fn&& fn, Args&&... args); // with return type (2) template <class Ret, class Fn, class... Args> /* unspecified */ bind(Fn&& fn, Args&&... args);
举例:
#include<iostream> #include <functional> using namespace std; int Plus(int a, int b) { return a - b; } int main() { //表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定 function<int(int, int)> func1 = bind(Plus, placeholders::_1,placeholders::_2);//placeholders是占位对象 cout << func1(1, 2) << endl; //_1代表本来的第一个参数,_2代表本来的第二个参数 //这里调参数 function<int(int, int)> func3 = bind(Plus, placeholders::_2, placeholders::_1); cout << func3(1, 2) << endl; return 0; }
那么实际的作用在哪里呢?
比如包装器包装的是类的成员函数,传参的时候第一个总是类的匿名对象,写起来很麻烦。
#include<iostream> #include <functional> using namespace std; class Sub { public: int sub(int a, int b) { return a - b; } }; int main() { function<int(int, int)> func1 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2); cout << func1(1, 2) << endl;//这里本来是三个参数,但是第一个参数已经被绑定了,包装器的参数也不用写三个了,这里也不用传三个参数了 return 0; }