zoukankan      html  css  js  c++  java
  • C++2.0特性之一:变量模板(variable template)

    变量模板(variable template)是C++2.0的一个新特性,虽然他功能强大,但是在平时的代码中用得比较少。最近在侯捷老师的视频里学到这个知识点,这里简单说一下。

    和C++模板一样,变量模板也有函数模板和类模板,这种情况有非常多相似,就是作用对象不同。

    那么变量模板这个“变量”体现在哪里?①参数个数可变  ②参数类型可变

    也就是我,我们可以给一个函数传进去一个乱七八糟的的一包东西,这包东西的元素个数和元素类型都是不确定的!这确实是一个激动人心的功能,有时候我们确实需要这一种特性,比如说我们希望设计一个参数个数不定的min/max函数,这时候参数可变就派上用场了。(当然其实只是实现可变参数的同类型函数,initializer_list就足够了但是必须要加上一个花括号

    侯捷老师的视频里讲到七个例子,这里选几个来理解

    首先是函数模板

    变量模板最简单的用法,我们得思考既然这一包得个数和参数类型都是不确定得,那么我们怎样才能精确地拿出里面的每一个元素呢?这里给出一个解决办法:用递归。我们每一次递归中把一包元素分成一个和另外一包两份,即假设本次这一包是n+1个元素,那么我们处理第一个元素,那么剩下n个元素还没处理,我们就把这n个元素分成两份:1+(n-1)这两份,然后把这两份继续递归下去。那么下一层递归就是处理n-1中的第一个元素再继续递归,最后总有处理完的那一天。

    talk is cheap, show me the code,一看代码就容易理解了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 void printX() {
     5     cout<<"All done"<<endl;
     6 }
     7 
     8 template<typename T,typename... Types>
     9 void printX(const T& firstArg,const Types&... args) {
    10     cout<<firstArg<<endl;
    11     printX(args...);
    12 }
    13 
    14 int main()
    15 {
    16     printX(7.5,"hello",bitset<16>(377),42);
    17     return 0;
    18 } 

    OK,学懂了上面那个例子,我们也许想用变量模板做一些有趣的小东西,我们可以尝试用变量模板模拟C语言的printf函数。

    代码有注释,很好懂。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 //这个函数是处理最后没有参数了 
     5 void Myprintf(const char *s) {
     6     while (*s) {
     7         if (*s=='%' && *(++s)!='%')        //没有参数却还有%,参数不足 
     8             throw runtime_error("invalid format string: missing arguments");
     9         cout<<*s; 
    10         s++;    
    11     }
    12 }
    13 
    14 //这里是Args还有参数,那么继续做 % 的解析 
    15 template<typename T,typename... Args>
    16 void Myprintf(const char* s,T value,Args... args) {
    17     while (*s) {
    18         if ((*s)=='%' && *(++s)!='%') {        //解析到一个%,消耗一个参数 
    19             cout<<value;
    20             Myprintf(++s,args...);
    21             return;
    22         }
    23         cout<<*s;    //无关%的字符原样输出 
    24         s++;
    25     }
    26     throw logic_error("extra arguments provided to printf");
    27 }
    28 
    29 int main()
    30 {
    31     Myprintf("%d args first:%d %s %lf
    ",2,42,"Hello",3.1415926);
    32     Myprintf("%d args first:%d %s %lf %d",2,42,"Hello",3.1415926);    
    33     return 0;
    34 }
    例子2

    从这里可以看到,我们依然使用了递归。其实递归和变量模板的搭配将陪伴我们很久。

    比如这里就用变量模板函数实现我们一开头所说的:可变参数的max函数:非常简洁小巧

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 template<typename T>
     5 T maximum(T n) {
     6     return n;
     7 }
     8 
     9 template<typename T,typename... Args>
    10 int maximum(T first,Args... args) {
    11     return max(first,maximum(args...));
    12 }
    13 
    14 int main()
    15 {
    16     cout<<maximum(12,2,94,41,36,1)<<endl;
    17     return 0;
    18 } 
    例子3

    上面都是模板函数,现在到模板类。其实道理也一样,都是个数和类型都可变,也都是递归处理:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 //下面的两个struct都是模板类,函数里直接创建临时变量调用函数 
     5 
     6 //盘点IDX然后输出,然后创建新的struct就行递归 
     7 template<int IDX,int MAX,typename... Args>
     8 struct PRINT_TUPLE {
     9     static void print(ostream& os,const tuple<Args...>& t) {
    10         os<<get<IDX>(t)<<(IDX+1==MAX ? "" : ",");
    11         PRINT_TUPLE<IDX+1,MAX,Args...>::print(os,t);    //注意IDX+1 
    12     }
    13 };
    14 //什么都不干的PRINT_TUPLE,处理最后 
    15 template<int MAX,typename... Args>
    16 struct PRINT_TUPLE<MAX,MAX,Args...> {
    17     static void print(ostream& os,const tuple<Args...>& t) {
    18     }
    19 };
    20 
    21 //重载了<<运算符,重点是 PRINT_TUPLE
    22 template<typename... Args>
    23 ostream& operator << (ostream& os,const tuple<Args...>& t) {
    24     os<<"[";
    25     PRINT_TUPLE<0,sizeof...(Args),Args...>::print(os,t);
    26     return os<<"]";
    27 }
    28 
    29 int main()
    30 {
    31     cout<<make_tuple(7.5,string("hello"),42,bitset<16>(377));
    32     return 0;
    33 }
    例子4

    接下来,我们要用变量模板实现tuple,我们有继承版本和组合版本的,都是令人拍案叫绝的实现。

    继承版本的,构造函数把第一个元素(自己的)和后n-1个元素(继承自父类),这样不断继承不断新增一个元素得到我们想要的个数。

    其实原理不难理解,但是代码实现上还是有许多技巧的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 template<typename... Values> class tup;
     5 
     6 //最基础的那个基类,就是一个空的类 
     7 template<> 
     8 class tup<> {
     9 };
    10 /*
    11 tup<>
    12 tup<int>
    13 tup<int,double>
    14 tup<int,double,string>
    15 */ 
    16 template<typename Head,typename... Tail>
    17 //<Head,Tail...>继承自<Tail...> 
    18 class tup<Head,Tail...> : private tup<Tail...> {
    19 private:
    20     typedef tup<Tail...> inherited;        //父类的类型 
    21 protected:
    22     Head m_head;
    23 public:
    24     tup() {}
    25     //那么构造函数把第一个元素(自己的)和后n-1个元素(继承自父类) 
    26     tup(Head v,Tail... vtail) : inherited(vtail...),m_head(v) {
    27     }
    28     
    29     Head head() {
    30         return m_head;
    31     }
    32     inherited& tail() {
    33         return *this;
    34     }
    35 };
    36 
    37 int main()
    38 {
    39     tup<int,float,string> it1(42,66.3,"world");
    40     cout<<it1.head()<<endl;
    41     cout<<it1.tail().head()<<endl;
    42     cout<<it1.tail().tail().head()<<endl;
    43     return 0;
    44 }
    例子5

    组合版本的

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 template<typename... Values> class tup;
     5 
     6 //最基础的那个基类,就是一个空的类 
     7 template<> 
     8 class tup<> {
     9 };
    10 /*
    11 tup<>
    12 tup<int>
    13 tup<int,double>
    14 tup<int,double,string>
    15 */ 
    16 template<typename Head,typename... Tail>
    17 class tup<Head,Tail...> {
    18 private:
    19     typedef tup<Tail...> composited;
    20 protected:
    21     composited m_tail;    //注意这里,组合的关键 
    22     Head m_head;
    23 public:
    24     tup() {}
    25     //那么构造函数把第一个元素和后n-1个元素(新组合) 形成组合 
    26     tup(Head v,Tail... vtail) : m_tail(vtail...),m_head(v) {
    27     }
    28     
    29     Head head() {
    30         return m_head;
    31     }
    32     composited& tail() {
    33         return m_tail;
    34     }    
    35 }; 
    36 
    37 int main()
    38 {
    39     tup<int,float,string> it1(41,6.3,"hello");
    40     cout<<it1.head()<<endl;
    41     cout<<it1.tail().head()<<endl;
    42     cout<<it1.tail().tail().head()<<endl;
    43     return 0;
    44 } 
    例子6

    参考资料:

    侯捷老师的C++2.0课程:https://www.bilibili.com/video/BV11x411Z7zk

  • 相关阅读:
    jQuery---三组基本动画 show hide
    jQuery---表格全选案例
    jQuery---prop方法和表格全选案例
    jQuery---美女相册案例
    jQuery---属性操作
    jQuery---tab栏切换
    jQuery---Class操作
    jQuery---CSS操作
    jQuery---第一部分复习
    DirBuste 使用
  • 原文地址:https://www.cnblogs.com/clno1/p/13066631.html
Copyright © 2011-2022 走看看