本文參考侯捷老師的視頻:https://www.youtube.com/watch?v=TJIb9TGfDIw&list=PL-X74YXt4LVYo_bk-jHMV5T3LHRYRbZoH
以及C++ primer第五版 相關內容。
可變參數模板函數
//遞歸的終止條件
void print() {}
//Variadic Templates
//一般用于遞歸處理
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args) {cout << firstArg << endl;print(args...);
}
上面這個模板函數可以接收任意個數任意類型的參數并進行打印輸出。其中typename... Types
是可變模板參數,表示可以接收任意類型,Types... args
表示可變函數參數,表示可以接收任意個數、任意類型的參數,要注意...
在不同的位置,這些都是固定的。可以看到我們輸出第一個參數以后,調用了print(args...)
,如果args
中的參數個數大于0,則會繼續調用模板函數,將第一個參數賦給firstArg
,其余參數賦給args
;如果參數個數等于0,則調用非模板函數void print()
結束遞歸。
可以看出,可變參數模板函數是通過遞歸來進行參數解包的。
特殊的,如果我們將上面的函數寫成下面的版本:
//遞歸的終止條件
template<typename T>
void print(const T& t) {cout << t << endl;
}
//Variadic Templates
//一般用于遞歸處理
template<typename T, typename... Types>
void print(const T& t, const Types&... args) {print(t);print(args...);
}
同樣完成將所有參數進行打印的工作。但是可能會有疑惑,對于print(t)
,似乎既可以調用void print(const T& t)
,又可以調用void print(const T& t, const Types... args)
,那么編譯器會調用哪一個呢?這個是確定的,編譯器會調用void print(const T& t)
。原因在于非可變參數版本比可變參數版本更加特例化。(詳細模板重載規則見C++ primer 16.3 重載與模板)
在可變參數模板函數中,我們可以通過sizeof...(args)
獲得可變參數的個數,返回一個常量表達式。
如果運行下面的代碼,仍然會和上面的有同樣的效果,void print(const Types... args)
不會運行:
//遞歸的終止條件
template<typename T>
void print(const T& t) {cout << t << endl;
}
//Variadic Templates
//一般用于遞歸處理
template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args) {print(firstArg);print(args...);
}template<typename... Types>
void print(const Types&... args) {cout << sizeof...(args) << endl;
}
可變參數模板類
最經典的就是tuple
,通過遞歸地繼承自己,實現了可以包含任意個數、任意類型的數據結構。