1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 【C++】C++11新特性——可变参数模板|function|bind

【C++】C++11新特性——可变参数模板|function|bind

时间:2023-10-02 07:42:26

相关推荐

【C++】C++11新特性——可变参数模板|function|bind

文章目录

一、可变参数模板1.1 可变参数的函数模板1.2 递归函数方式展开参数包1.3 逗号表达式展开参数包1.4 empalce相关接口函数 二、包装器function2.1 function用法2.2 例题:逆波兰表达式求值2.3 验证 三、绑定函数bind3.1 调整参数顺序3.2 固定绑定参数

一、可变参数模板

在C语言中其实也有可变参数:

1.1 可变参数的函数模板

C++库里面也有很多使用可变参数函数模板的:

template <class ...Args>void fun(Args... args){}

Args是一个模板参数包,args是一个函数形参参数包

声明一个参数包Args…args,这个参数包中可以包含0到任意个模板参数

以前只能传递一个对象做参数,有了可变参数包就可以传递0~n个参数:

template <class ...Args>void fun(Args... args){// 获取参数包中有几个参数cout << sizeof...(args) << endl;}int main(){fun();fun(1);fun(1, 1.1);fun(1, 1.1, std::string("abc"));std::vector<int> v;fun(1, 1.1, std::string("abc"), v);return 0;}

那么怎么把这些参数取出来呢?

1.2 递归函数方式展开参数包

void fun(){cout << endl;}template <class T, class ...Args>void fun(T val, Args... args){cout << val << " ";fun(args...);}int main(){fun();fun(1);fun(1, 1.1);fun(1, 1.1, std::string("abc"));return 0;}

解释:

按照箭头的方式调用,最后当没有参数的时候就会走最上面的函数

1.3 逗号表达式展开参数包

template <class T>void printArg(T val){cout << val << " ";}template <class ...Args>void fun(Args... args){int arr[] = {(printArg(args), 0)... };cout << endl;}int main(){fun(1, 1.1, std::string("abc"));return 0;}

这种展开参数包的方式,不需要通过递归终止函数,printArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。

(printArg(args), 0):先执行printArg(args),再得到逗号表达式的结果0。通过初始化列表来初始化一个变长数组,{(printArg(args), 0)...}将会展开成((printArg(arg1),0),(printArg(arg2),0),(printArg(arg3),0), etc... ),最终会创建一个元素值都为0的数组。在创建数组的过程中会先执行逗号表达式前面的部分printArg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

1.4 empalce相关接口函数

比较:

insert

emplace

emplace的使用:

int main(){std::list<int> lt;lt.emplace_back();lt.emplace_back(1);lt.emplace_back(2);for (auto& e : lt){cout << e << " ";}cout << '\n';return 0;}

而emplace在插入自定义类型数据的时候会有区别:

struct A{A(int a = 1, double b = 2): _a(a), _b(b){}int _a;double _b;};int main(){std::list<A> lt;lt.push_back({1, 1.0 });lt.emplace_back(2, 2.0);//lt.push_back(3, 3.0);// errorfor (auto& e : lt){cout << e._a << endl;}cout << '\n';return 0;}

上面使用push_back是先构造再拷贝构造,而使用emplace_back就可以直接构造(使用参数包)。

验证一下:

引入之前写过的string类

namespace yyh{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""): _size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];// _capacity表示有效字符个数strcpy(_str, str);cout << "string(const char* str) -- 构造函数" << endl;}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s): _size(strlen(s._str)), _capacity(s._size){_str = new char[_capacity + 1];strcpy(_str, s._str);cout << "string(const string& s) -- 深拷贝" << endl;}// 移动构造string(string&& s){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;};}struct A{A(int a = 1, const char* str = " "): _a(a), _str(str){}int _a;yyh::string _str;};

push_back

emplace_back

总结:

如果是左值,使用push_back或者emplace_back没什么区别。

对于右值,emplace_back把构造和拷贝构造合二为一成构造函数

但是如果没有移动拷贝效率差距会很大emplace_back还是直接构造,但是push_back就会走深拷贝。

二、包装器function

其实包装器function就是一个类模板。先来看一段代码:

template <class F, class T>void func(F fun, T val){static int cnt = 1;cout << "cnt: " << cnt++ << endl;cout << "&cnt: " << &cnt << endl;}int f1(int x){return x * 2;}struct f2{int operator()(int x){return x * 2;}};int main(){// 函数名func(f1, 2);// 仿函数对象func(f2(), 2);// lambda表达式func([](int x)->int {return x * 2; }, 2);return 0;}

可以看到以三种不同的方式调用func函数,func函数就会被实例化出三份

包装器可以很好的解决上面的问题

2.1 function用法

如果参数是两个int的话就是:

function<int(int, int)> fun1

int f1(int x){return x * 2;}struct f2{int operator()(int x){return x * 2;}};class f3{public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}};int main(){// 普通函数function<int(int)> fun1(f1);cout << fun1(2) << endl;// 仿函数function<int(int)> fun2;fun2 = f2();cout << fun2(2) << endl;// lambda表达式function<int(int)> fun3;fun3 = [](int x)->int {return 2 * x; };cout << fun3(2) << endl;// 静态成员函数指针function<int(int)> fun4 = &f3::muli;cout << fun4(2) << endl;// 非静态成员函数指针function<int(f3/*this指针*/, int)> fun5 = &f3::muld;cout << fun5(f3(), 2) << endl;return 0;}

这里要注意类成员函数的调用方法:

对于静态成员函数,因为没有this指针,所以正常调用,后面也可以不加&

对于非静态成员函数,因为含有this指针,而this指针不能显示传递,所以要传递对象必须加&

当然也可以不在()内部加上对象,可以使用lambda表达式中的[]捕获:

f3 ff;function<int(int)> fun6 = [&ff](int x)->double {return ff.muld(x); };

2.2 例题:逆波兰表达式求值

题目链接

具体做法就不多叙述,这里主要展示怎么使用function函数:

class Solution {public:int evalRPN(vector<string>& tokens) {stack<int> st;map<string, function<int(int, int)>> hash = {{"+", [](int x, int y)->int{return x + y;}},{"-", [](int x, int y)->int{return x - y;}},{"*", [](int x, int y)->int{return x * y;}},{"/", [](int x, int y)->int{return x / y;}},};for(auto& e : tokens){if(hash.count(e) == 0){st.push(stoi(e));}else{int right = st.top();st.pop();int left = st.top();st.pop();st.push(hash[e](left, right));}}return st.top();}};

2.3 验证

在2.1中我们看道以那种方式会实例化三个模板函数。

而我们可以用function来解决这个问题:

template <class F, class T>T func(F fun, T val){static int cnt = 1;cout << "cnt: " << cnt << endl;cout << "&cnt: " << &cnt << endl;return fun(val);}int f1(int x){return x * 2;}struct f2{int operator()(int x){return x * 2;}};class f3{public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}};int main(){// 函数名func(function<int(int)>(f1), 2);// 仿函数对象f2 ff;func(function<int(int)>(ff), 2);// lambda表达式func(function<int(int)>([](int x)->int {return x * 2; }), 2);return 0;}

可以看出只实例化出了一份函数

三、绑定函数bind

3.1 调整参数顺序

int Plus(int a, int b){return a - b;}int main(){function<int(int, int)> fun1 = bind(Plus, placeholders::_1, placeholders::_2);cout << fun1(1, 2) << endl;function<int(int, int)> fun2 = bind(Plus, placeholders::_2, placeholders::_1);cout << fun2(1, 2) << endl;return 0;}

从这里就可以看出_1代表第一个参数,_2代表第二个参,对于fun2就相当于把传参的顺序改变了

3.2 固定绑定参数

class fun{public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}};int main(){// 非静态成员函数指针function<int(fun/*this指针*/, int)> fun1 = &fun::muld;cout << fun1(fun(), 2) << endl;return 0;}

上面说过了使用非静态成员函数的时候得传递对象进去。如果我们不想传递这个参数呢?

class fun{public:static int muli(int x){return x * 2;}double muld(double x){return x * 2;}};int main(){// 非静态成员函数指针function<int(fun/*this指针*/, int)> fun1 = &fun::muld;cout << fun1(fun(), 2) << endl;// 绑定参数function<int(int)> fun2 = bind(&fun::muld, fun(), std::placeholders::_1);cout << fun2(2) << endl;return 0;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。