• 文章
  • C++11 - 新特性 - 變參模板
釋出
2012年5月3日 (最後更新:2012年5月31日)

C++11 - 新特性 - 變參模板

評分: 3.7/5 (386票)
*****

簡介

在新的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