c++ 折叠参数包详解(悄然增强编程效率)
前言
本节将为大家带来折叠参数包的详细讲解,折叠参数包为c++模板编程提供了更加灵活和强大的工具,可以提高代码的简洁性和可读性,看完后希望对你有收获
一、介绍
折叠参数就是一个参数包, 代表是多个未知,tuple元组就是一个折叠参数的使用
折叠参数类型:
- typename ...args: args参数包的包名 ,本质是声明一个args折叠参数类型
- args ...arg: 折叠参数包类型的变量
- ...:理解为多个意思
二、函数模板中使用折叠参数
1、递归方式展开
递归方式的展开是比较好理解的,每一次调用第二个print函数就打印一次data,然后又调用自己,这时候参数包也剥离了一个参数,也就是调用自己会打印下一个data
有的同学看了下面的代码可能会疑惑,为什么会有两个函数,这是因为上面的函数为终止函数,也就是当第二个函数参数包中只有一个参数时调用第一个函数
template <typename _ty> void print(_ty data) { cout << data << endl; } template <typename _ty,typename ...args> void print(_ty data, args ...args) { cout << data << "\t"; print(args...); }
2、列表数据展开
这个的难点和重点在于initializer_list<int>{(printdata(args), 0)...};,这一行代码用到了列表和逗号表达式的特性,不用说列表的每个值最后都被初始化为0,但是列表的每个值被初始化为0的时候,他们会先执行printdata(args(n)),也就是会不断打印,参数包不断展开
template <typename _ty> void printdata(_ty data) { cout << data << "\t"; } template <typename ...args> void printargs(args ...args) { initializer_list<int>{(printdata(args), 0)...}; cout << endl; }
3、完美转发的方式展开
完美转发一般是用来统一接口,也就是有许多函数,他们的参数数量、类型不同,我们把他们统一为只用函数名就可以调用该函数,且不减少其原功能
这里我们用仿函数接收一下用bind绑定的函数以及参数包,注意这里函数和参数包绑定的时候都用了完美转发
什么是完美转发呐?forword是为了解决在函数模板中,使用右值引用参数(t&&),传递右值进去以后,类型会变为左值的问题。当传入的参数是一个对象时,右值变左值就会出问题,因为左值调用拷贝构造,右值调用移动构造。本来可以用移动构造提高效率,却因为右值变成左值,调用了拷贝构造。所以我们要把它变回去!实参传的是右值,进入函数体还是右值,这就是完美转发
class test { public: void printk() { if (func) func(); } template <typename func,typename ...args> void connect(func&& f, args&& ...args) //右值引用 { func = bind(forward<func>(f), forward<args>(args)...); } protected: function<void()> func; }; void sum(int a, int b) { cout<< a + b; } int main() { test test; test.connect(sum, 1, 2); test.printk(); test.connect([](int a, int b) {cout << endl << a + b; }, 3, 8); test.printk(); return 0; }
上面的例子中通过connect绑定函数和参数包,实现统一接口的功能,通过printk函数调用
三、类模板中使用折叠参数
1、继承+模板特化的方式展开
类中实现折叠参数,前两个类是必须,对应上面的终止函数,继承的时候要写清楚public test<args...>,还有就是第三个类必须要一个无参构造函数,且带参数包的的构造函数初始化时要调用子类的构造函数,还有就是打印的时候要一层一层的,采用继承+模板特化就是一代一代的
template <typename ...args> class test; template<> class test<> {}; template <typename _ty, typename ...args> class test<_ty, args...> :public test<args...> { public: test() {} test(_ty data, args ...args) :data(data), test<args...>(args...) {} test<args...>& getobject() { return *this; } _ty& getdata() { return data; } protected: _ty data; }; void testone() { test<string, int, double> test("fsdjf", 32, 3.23); cout << test.getdata() << "\t" << test.getobject().getdata() << "\t" << test.getobject().getobject().getdata() << endl; }
2、递归的方式展开
递归这一种就是把自己当对象调用,其它的和上面相同
template <typename ...args> class my_tuple; template<> class my_tuple<> {}; template <typename _ty, typename ...args> class my_tuple<_ty, args...> { public: my_tuple() {} my_tuple(_ty data, args ...args) :data(data),args(args...) {} _ty& getdata() { return data; } my_tuple<args...>& getobject() { return *this; } protected: _ty data; my_tuple<args...> args; }; void testtwo() { test<string, int, double> test("fsdjf", 32, 3.23); cout << test.getdata() << "\t" << test.getobject().getdata() << "\t" << test.getobject().getobject().getdata() << endl; }
关于c++折叠参数包悄然增强编程效率的文章就介绍至此,更多相关c++折叠参数包内容请搜索硕编程以前的文章,希望以后支持硕编程!