可變參模板template


可變參數模板

原文鏈接: http://blog.csdn.net/xiaohu2022/article/details/69076281
普通模板只可以采取固定數量的模板參數。然而,有時候我們希望模板可以接收任意數量的模板參數,這個時候可以采用可變參數模板。對於可變參數模板,其將包含至少一個模板參數包,模板參數包是可以接收0個或者多個參數的模板參數。相應地,存在函數參數包,意味着這個函數參數可以接收任意數量的參數。

使用規則

一個可變參數類模板定義如下:

template<typename ... Types>
class Tuple
{};

可以用任意數量的類型來實例化Tuple:

Tuple<> t0;
Tuple<int> t1;
Tuple<int, string> t2;
// Tuple<0> error;  0 is not a type

 

如果想避免出現用0個模板參數來實例化可變參數模板,可以這樣定義模板:

template<typename T, typename ... Types>
class Tuple
{};

 

此時在實例化時,必須傳入至少一個模板參數,否則無法編譯。
同樣地,可以定義接收任意參數的可變參數函數模板:

template<typename ... Types>
void f(Types ... args);

cout<<sizeof...(args)<<endl;//打印可變參數的個數

// 一些合法的調用
f();
f(1);
f(3.4, "hello");

 

 

對於類模板來說,可變模板參數包必須是模板參數列表中的最后一個參數(即模板列表參數中的最后一個參數必須是可變的)。但是對於函數模板來說,則沒有這個限制,考慮下面的情況:

template<typename ... Ts, typename U>
class Invalid
{};   // 這是非法的定義,因為永遠無法推斷出U的類型

template<typename ... Ts, typename U>
void valid(U u, Ts ... args);  // 這是合法的,因為可以推斷出U的類型
// void invalid(Ts ... args, U u); // 非法的,永遠無法推斷出U

valid(1.0, 1, 2, 3); // 此時,U的類型是double,Ts是{int, int, int}

 

可變參數函數模板實例

無法直接遍歷傳給可變參數模板的不同參數,但是可以借助遞歸的方式來使用可變參數模板。可變參數模板允許創建類型安全的可變長度參數列表。下面定義一個可變參數函數模板processValues(),它允許以類型安全的方式接受不同類型的可變數目的參數。函數processValues()會處理可變參數列表中的每個值,對每個參數執行對應版本的handleValue()。

// 處理每個類型的實際函數
void handleValue(int value) { cout << "Integer: " << value << endl; }
void handleValue(double value) { cout << "Double: " << value << endl; }
void handleValue(string value) { cout << "String: " << value << endl; }

// 用於終止迭代的基函數
template<typename T>
void processValues(T arg)
{
    handleValue(arg);
}

// 可變參數函數模板
template<typename T, typename ... Ts>
void processValues(T arg, Ts ... args)
{
    handleValue(arg);
    processValues(args ...); // 解包,然后遞歸
}

 

可以看到這個例子用了三次... 運算符,但是有兩層不同的含義。用在參數模板列表以及函數參數列表,其表示的是參數包。前面說到,參數包可以接受任意數量的參數。用在函數實際調用中的...運算符,它表示參數包擴展,此時會對args解包,展開各個參數,並用逗號分隔。模板總是至少需要一個參數,通過args...解包可以遞歸調用processValues(),這樣每次調用都會至少用到一個模板參數。對於遞歸來說,需要終止條件,當解包后的參數只有一個時,調用接收一個參數模板的processValues()函數,從而終止整個遞歸。

假如對processValues()進行如下調用:

processsValues(1, 2.5, "test"); 

其產生的遞歸調用如下:

processsValues(1, 2.5, "test");
    handleValue(1);
    processsValues(2.5, "test");
        handleValue(2.5);
        processsValues("test");
            handleValue("test");

 

 

#include <iostream>
#include<string>
#include<string.h>
#include <memory>
using namespace std;
// 用來終止遞歸並處理包中最后一個元素 
template <typename T>
void print(T t)
{
    cout << t;
}

// 包中除了最后一個元素之外的其他元素都會調用這個版本的print 
template <typename T, typename...Types>
void print(T t, Types ...Arg)
{
    cout << t << endl;   // 打印第一個實參 
    print(Arg...);    // 遞歸調用,打印其他實參 
}

// 測試 
int main()
{
    print("string1", 2, 3.14f, "string2", 42);
    cout << endl;
    system("pause");
    return 0;
}

 

類模板的全特化:就是模板中的參數類型都確定了

 

類模板的偏特化:就是模板中有些參數確定了,有些沒有確定

 

注意:模板的全特化和偏特化都要在類模板的基礎上實現

//先定義類模板
  template<typename T1, typename T2>
  class Test
  {
  public:
      Test(T1 i, T2 j) :a(i), b(j) { cout << "模板類" << endl; }
  private:
      T1 a;
      T2 b;
  };
  //模板的全特化
  template<>
  class Test<int, char>
  {
  public:
      Test(int i, char j) :a(i), b(j) { cout << "全特化" << endl; }
  private:
      int a;
      char b;
  };
  //模板的偏特化
  template <typename T2>
  class Test<char, T2>
  {
  public:
      Test(char i, T2 j) :a(i), b(j) { cout << "偏特化" << endl; }
  private:
      char a;
      T2 b;
  };




int main()
{
    string s;
    //調用類模板
    Test<int, int> A(1, 1);
    //模板的全特化
    Test<int, char> B(1, 'c');
    //模板的偏特化
    Test<char, int>  C('c', 1);
    system("pause");
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM