在很早已前的 C 就有提供不定數量參數 (variadic argument) 的功能了,比方說像 printf/scanf 系列的 function,他們能接受數量不固定的參數。而 C++11 把這個功能也放到 template 來用了,所以現在也有 variadic template 的功能,有些資料的關鍵字是 parameter pack,不過其實內容是一樣的。
variadic template 長得像這樣 (其實跟 variadic argument 滿像的):
template <typename ... Args> void func(Args ... args);
重點就在於 <> 中的 typename,以前寫 template 時這邊必須要是寫死的,現在可以用 ... 來表示不定數量的參數型態。這樣有甚麼好處呢?比方說早期 STL 只有像是 std::pair 能同時允許 2 個 element 擁有不同型態,如果想要 2 個以上的 element 可以是相同或不同的型態就辦不到。而 C++11 後的 STL 多了一個 std::tuple 可以接受不定數量且不定型態的 element,像是 std::tuple<int, char>、std::tuple<std::string, bool, char> 這樣,數量跟型態都是不固定的,這其中就是用到 variadic template 的效果。我們可以看到 std::tuple 宣告其實長得像這樣:
template <class... Types> class tuple;
print(1, 2.5, 'x', "123");
1 , 2.5 , x , 123
print(1);
1
void print() {}
template <typename T> void print(const T& arg)
{
std::cout << arg;
}
當然,當參數數量多於一個時,我們可以寫成 template <typename T, typename ... Args> void print(const T& t, Args&& ... args)
{
std::cout << t << " , ";
print(args...);
}
可以特別注意到上面的寫法,第一個參數的型態 T 是有被另外獨立出來的,這樣可以確保 template 在推導參數型態時把第一個參數 t 獨立,而不會跟其他參數綁住,如此一來我們就能直接存取 t。
那剩餘的參數要怎麼印呢? 可以看到 std::cout 下面那行的寫法:
print(args...);
對,就是用遞迴。首先,第一個參數 t 被排除掉了,其他所有的參數就可以寫成 args... 來代表 (... 是正式語法,不要真的省略掉阿 XD),當 args... 實際上只剩 1 個時就會直接呼叫到上面寫得只有一個參數的特化版本去了。
當然,如果有個 function 本身就是能接受不定數量參數的就不用自己寫遞迴了,直接呼叫就好
沒有留言:
張貼留言