C++可變參數模板(variadic template)詳細介紹及代碼舉例html
C++11 語言核心的改進中,最爲關注的有 rvalue reference,lambda,variadic template。rvalue 規則稍微複雜,但一旦理解和記住了,應用上就沒有什麼困難。lambda 實際上是一個「很天然」的語言設施,除了語法稍顯詭異以外,習慣了就能立刻用上,並且是能普遍用上的好東西。variadic template 這個新特性不像前二者,它自己的語法規則並不複雜,可是應用的時候確比較費腦子,好在這個技術主要是用在庫的實現中,一個實現得好的庫,並不會讓咱們對實現的細節做任何的要求。可變參數模板(variadic template)特性自己是一個很天然的需求,它完善了 C++ 的模板設計手段。原來的模板參數可使類和函數的參數類型「任意化」,若是再加上「參數個數的任意化」,那麼在參數方面的設計手段就基本上齊備了,有了variadic template 顯然可讓設計出來的函數或是類有更大的複用性。由於有不少處理都是與「處理對象的個數」關係不大的,好比說打屏(printf),好比說比較大小(max,min),好比函數綁定子(bind,function要對應各類可能的函數就要能「任意」參數個數和類型)。若是不能對應任意個參數,那麼就總會有人沒法重用已有的實現,而不得再也不重複地寫一個本身須要的處理,而共通庫的實現者爲了儘量地讓本身寫的類(函數)能複用在更多的場景,也不得不重複地寫不少的代碼或是用詭異的技巧,宏之類的去實現有限個「任意參數」的對應。(像TR1中的 bind 等)。ios
1、C++可變參數模板(variadic template)基本語法c++
聲明一個帶有可變參數個數的模板的語法以下所示:程序員
template<typename Element> class tuple;web
tuple<int, string> a; // use it like this安全
在模板參數 Element 左邊出現省略號 ... ,就是表示 Element 是一個模板參數包(template type parameter pack)。parameter pack(參數包)是新引入 C++ 中的概念,好比在這個例子中,Element 表示是一連串任意的參數打成的一個包。好比第2行中,Element 就是 int, string這個參數的合集。不只「類型」的模板參數(也就是typename定義的參數)能夠這樣作,非類型的模板參數也能夠這樣作。好比下面這個例子:ide
template<typename T, unsigned PrimaryDimesion, unsigned.. Dimesions>函數
class array { /**/ };this
array<double, 3, 3> rotation_matrix; //3x3 ratiation matrixspa
如今咱們知道parameter pack了,怎麼在程序中真正具體地去處理打包進來的「任意個數」的參數呢?我原來覺得,編譯器會提供一些像get_param<1>
(Element) 之類的內建的「參數抽取函數」給程序員使用結果不是!!看來個人思路仍是太「過程式了」。其實 C++11 用的是 unpack 和相似函數重載似的「模板特化」來抽取參數的。這是應用 variadic tempate 最「坑爹」的部分,由於它要求對「遞歸」和「人肉代碼展開」有必定的功力啊。仍是看例子吧:
template<typename... Elements> class tuple;
template<typename Head, typename... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
Head head;
public:
/* implementation */
};
template<>
class tuple<> {
/* zero-tuple implementation */
};
第1行聲明瞭一個能夠對應任意參數的tuple類,第2行到7行聲明瞭這個類的一個部分特化,注意,這就是抽取參數的典型方法了。
只說明一下針對 parameter pack 相對的另外一個概念,模板參數後面帶省略號 ... 就是一個解包(unpack),會把這個參數所表示的參數列表解開後去匹配新的模板,或是進行模板展開。
2、C++可變參數模板舉例
新的標準庫裏,有不少個庫都直接依賴於 variadic template 這個語言特性,好比,tuple,bind,function。但他們比較複雜,也不夠「驚豔」。C++ 老爸的 C++11 的 FQA 和 Wikipedia 的例子都是「類型安全」的printf.
C++可變參數模板實例代碼1
void printf(const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
// call even when *s == 0 to detect extra arguments
printf(s + 1, args...);
return;
}
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
C++可變參數模板實例代碼2
/*
* g++ myPrintf.cpp -o myPrintf -std=c++0x -Wall
* 來源: 169it
*/
#include <iostream>
#include <cstdlib>
#include <stdexcept>
void myPrintf(const char * s)
{
while (*s)
{
if (*s == '%')
{
if (*(s + 1) == '%')
{
++s;
}
else
{
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void myPrintf(const char * s, T value, Args... args)
{
while (*s)
{
if (*s == '%')
{
if (*(s + 1) == '%')
{
++s;
}
else
{
std::cout << value;
myPrintf(s + 1, args...); // 即使 *s == 0 的時候,也調用,以便用於檢測多餘的參數。
return;
}
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to myPrintf");
}
int main ( )
{
// 每個百分號,輸出一個參數
myPrintf( "a%bcde%fghij%kl%mn\n", 12, "interesting", 8421, "very_interesing" );
return EXIT_SUCCESS;
}