C++11變長模板

C++11變長模板

簡介

該篇博客主要介紹C++11中的變長模板,對變長模板的原理和使用方法進行介紹。本篇博客參考書籍深入理解C++11新特性解析與應用一書,非常推薦該書作爲C++11學習的參考資料,英語好的話更推薦直接閱讀C++官網中的關於C++11新特性的介紹。
該本書我已經上傳高清pdf版本,並且包含非常詳細的書籤,下載地址如下(爲上傳修改了名稱,放心下載):
C++11新特性解析與應用

模板和函數參數包

模板參數包說明

變長類模板的形式如下:

template <typename... Elements> class tuple;

標識符Elements之前的使用了省略號來表示該參數是變長的。在C++11中,Elements就被稱作是一個模板參數包(template parameter pack),這是一種新的模板參數類型。它可以接受任意多個參數作爲模板參數,實例化的tuple模板類如下所示:

tuple<int, char, double>

與普通的模板參數類似,模板參數包也可以是非類型的,例如

template<int... A> class NonTypeVT { };
NonTypeVT<1,0,2> vtvt;

除了類型的模板參數包和非類型的模板參數包,模板參數包實際上還是模板類型的,後面討論。

解包

爲了使用模板參數包,我們需要將其解包(unpack)。在C++11中,通過**包拓展(pack expansion)**的表達式來完成。例如:

template<typename... A> class Template: private B<A...> { };

這裏的表達式A...就是一個包拓展。參數包會在包拓展的位置展開爲多個參數。比如:

template<typename T1, typename T2> class B {};
template<typename... A> class Template: private B<A...> { };
Template<X, Y> xy;

上面的列子中展示了包拓展的實現,但是該例子基於類模板B總是接受兩個參數的前提下。若我們在這裏聲明瞭一個Template<X, Y, Z>,就必然會發生模板推導的錯誤。爲了解決上述問題,見後面內容中提出的解決辦法。

在可變參數模板中使用遞歸

通過定義遞歸的模板偏特化定義,我們可以使得參數包在實例化時能夠層層展開,直到參數包中的參數逐漸耗盡或到達某個數量的邊界爲止。
類模板使用遞歸代碼如下所示(模板參數包):

template<typename... Elements> class tuple; // 變長模板的聲明

template<typename Head, typename... Tail>           // 遞歸的偏特化定義
class tuple<Head, Tail...> : private tuple<Tail...> {
	Head head;
}

template<> class tuple<> {};  // 邊界條件

上述代碼中偏特化版本的tuple包含了兩個參數,一個是類型模板參數Head,另一個則是模板參數包Tailtuple<double, int, char, float>會引起基類的遞歸構造,這樣的遞歸構造在tuple的參數包爲0個的時候會結束。這是由於我們定義了邊界條件,即編譯器從tuple<>建造出tuple<float>


函數模板中使用地推代碼如下所示(函數參數包):

// definition for 0 parameters -- terminating call
void show_list3() {}

// definition for 1 or more parameters
template<typename T, typename... Args>
void show_list3( T value, Args... args)
{
    std::cout << value << ", ";
    show_list3(args...); 
}

模板參數包與函數參數包的區別是在C++11中,標準要求函數參數包必須唯一,且是函數的最後一個參數,而模板參數包則沒有這樣的要求。

進階

C++11標準定義了以下7種參數包可以展開的位置:

  • 表達式
  • 初始化列表
  • 基類描述列表
  • 類成員初始化列表
  • 模板參數列表
  • 通用屬性列表
  • lambda函數的捕捉列表

不同的包拓展方式

template<typename... A> class T: private B<A>... { };  // (1)
template<typename... A> class T: private B<A...> { };  // (2)

上述兩種在基類描述列表中解包後是不同的,對於同樣的實例化T<X, Y>,解包後的形式如下:

class T<X, Y>: private B<X>, private<Y> { };  // (1)
class T<X, Y>: private B<X, Y> { };  // (2)

類似的狀況也會發生在函數模板中。

template<class... A> int Vaargs(A... args) {
	int size = sizeof...(A);
}

在C++11中引入了新操作符sizeof...,它的作用是計算參數包中的參數個數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章