簡介
在新的
C++語言標準C++11出現之前,
模板的使用在實現
函式物件(仿函式)和
tuple功能等方面相當有限。使用早期C++標準實現這類東西通常需要重複編寫相似的程式碼,並且不能忘記
預處理器超程式設計。然而,得益於
變參模板,使用
模板程式設計新特性變得
更簡單、
更清晰且
更節省記憶體。
雖然
D程式語言也提供了
變參模板,但本文只涵蓋C++11標準提供的變參模板,因此閱讀和理解本文不需要了解D程式語言的變參模板。但假設讀者瞭解類模板和函式模板是什麼,以及如何宣告、定義和使用它們。
什麼是變參模板?
變參模板是一種可以接受任意數量、任意型別的模板引數的模板。類和函式都可以是變參的。這是一個變參類模板
1 2
|
template<typename... Arguments>
class VariadicTemplate;
|
以下任何一種建立此類模板例項的方式都是有效的
1 2 3
|
VariadicTemplate<double, float> instance;
VariadicTemplate<bool, unsigned short int, long> instance;
VariadicTemplate<char, std::vector<int>, std::string, std::string, std::vector<long long>> instance;
|
變參模板引數的數量也可以為零,因此以下
VariadicTemplate<> 例項;
在C++11中也是有效的。
但是,如果您建立如下模板
1 2
|
template<typename T, typename... Arguments>
class VariadicTemplate;
|
您必須至少設定一個型別作為模板引數(
對於typename T),除非已經初始化了預設型別,如下宣告
1 2
|
template<typename T = int, typename... Arguments>
class VariadicTemplate;
|
語法 - 省略號運算子 (...):
省略號運算子 (...) 是C++中在不同上下文中使用的
運算子。它的名字來源於
C中的
省略號機制。在這種機制下,程式設計師可以建立一個接受可變數量引數的函式。在C和C++中,最常利用這種機制的函式是
C標準庫中的
printf函式。
int printf (const char* format, ... );
省略號機制也可以與
預處理器以
宏的形式使用。接受可變數量引數的宏稱為
變參宏。
#define VARIADIC_MACRO(...)
在
C++中,這個
省略號運算子在
異常處理這一不同上下文中有了新的含義。該運算子用在
try塊之後的
catch塊中。
1 2 3 4 5 6
|
try{
// Try block.
}
catch(...){
// Catch block.
}
|
在這裡,
省略號運算子表示
catch塊接受來自
try塊的任何丟擲的異常作為其引數,而不考慮型別。
在C++11中,
變參模板為該運算子帶來了又一個含義。該運算子的工作方式與前面所述的省略號機制有些類似,但更復雜一些。
1 2
|
template<typename... Arguments>
void SampleFunction(Arguments... parameters);
|
這是一個函式模板。變參模板引數的內容稱為
引數包。這些包隨後在函式引數內部解包。例如,如果您呼叫前面的變參函式模板……
SampleFunction<int, int>(16, 24);
……一個等效的函式模板將是這樣的
1 2
|
template<typename T, typename U>
void SampleFunction(T param1, U param2);
|
語法 - sizeof... 運算子 (sizeof...):
與變參模板一起使用的另一個運算子是
sizeof...運算子。與用於確定型別大小的
sizeof運算子(
例如 sizeof(int) 或 sizeof(double))不同,
sizeof...運算子可用於確定傳遞給變參模板的型別的數量。這可以透過以下方式實現
1 2 3 4 5
|
template<typename... Arguments>
class VariadicTemplate{
private:
static const unsigned short int size = sizeof...(Arguments);
};
|
語法 - 兩個省略號運算子一起使用 (......):
在某些情況下,可以將兩個省略號運算子放在一起
(......)。這兩個運算子也可以分開寫
(寫成 ... ...)。
然而,最清晰的方式可能是用逗號將這兩個運算子分開
(..., ...)。無論帶逗號還是不帶逗號的寫法都是可接受的。
這種語法可能出現在使用省略號機制的變參函式模板中。
1 2 3 4
|
template<typename... Arguments>
void SampleFunction(Arguments......){
}
|
如前所述,這兩個省略號運算子組合在一起可以有不同的寫法,所以以下示例
1 2 3 4
|
template<typename... Arguments>
void SampleFunction(Arguments... ...){
}
|
1 2 3 4
|
template<typename... Arguments>
void SampleFunction(Arguments..., ...){
}
|
也有效。
變參模板的用途 - 繼承和初始化列表:
對於類而言,變參模板可用於
繼承和
初始化列表。利用變參模板的繼承可以透過以下方式實現
1 2
|
template<typename... BaseClasses>
class VariadicTemplate : public BaseClasses...
|
另外,如果我們想在這個類中使用初始化列表來呼叫所有給定的基類作為模板引數的建構函式,我們必須這樣做
1 2 3 4 5 6 7
|
template<typename... BaseClasses>
class VariadicTemplate : public BaseClasses...{
public:
VariadicTemplate(BaseClasses&&... base_classes) : BaseClasses(base_classes)...{
}
};
|
正如您所見,C++11在建構函式的引數列表中引入了一個新運算子——
rvalue運算子(&&),它允許
rvalue引用。本文無意涵蓋此運算子的用法,但有關如何使用此運算子(
以及rvalue引用的一般用法)的資訊,請訪問此連結
http://thbecker.net/articles/rvalue_references/section_01.html
變參模板的用途 - 變參類模板特化:
與類模板一樣,變參類模板也可以被特化。對於模板,特化是這樣發生的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
template<typename T>
class Template{
public:
void SampleFunction(T param){
}
};
template<>
class Template<int>{
public:
void SampleFunction(int param){
}
};
|
但對於變參模板,則是這樣的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
template<typename... Arguments>
class VariadicTemplate{
public:
void SampleFunction(Arguments... params){
}
};
template<>
class VariadicTemplate<double, int, long>{
public:
void SampleFunction(double param1, int param2, long param3){
}
};
|
相關連結:
如果您對檢視利用變參模板的C++11標準類模板感興趣,請檢視上面提到的
tuple,連結如下
http://www.cplusplus.com/reference/std/tuple/tuple/
變參模板可能派上用場的另一個領域是
委託。如果您已經熟悉
託管C++和/或
C#,那麼學習C++委託可能不是問題。您可能會在C++中找到它們的良好用途。
結論:
模板一直是C++中的一個強大功能。現在,在引入變參模板之後,模板被證明更加強大。變參模板是實現委託和tuple的可靠解決方案。而且,與C風格的省略號機制相比,變參模板可以提供一種更型別安全的方式來替代它們。
更多資訊 - 編譯器支援
如果您不熟悉或不瞭解您的編譯器功能支援情況,請訪問此連結
http://wiki.apache.org/stdcxx/C++0xCompilerSupport
特別感謝
- 上帝
- 所有有用的資源
(請參見下方)資源
文字資源
http://digitalmars.com/d/1.0/variadic-function-templates.html
http://en.wikipedia.org/wiki/Variadic_template
http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
http://linuxprograms.wordpress.com/2008/03/07/c-ellipsis-operator-printf/
http://stackoverflow.com/questions/5625600/what-is-the-meaning-of-token/5625782#5625782
http://stackoverflow.com/questions/7767202/template-specialization-with-variadic-templates
http://www.cplusplus.com/doc/tutorial/exceptions/
http://www.devx.com/cplus/Article/41533
http://www.fredosaurus.com/notes-cpp/expressions/sizeof.html
http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html