在C++11之前, 有兩個典型的受制於模板功能不強而導致代碼重復難看的問題, 那就 function object 和 tuple。 拿 function objects 來說, 需要一個返回類型參數及N個參數類型參數。 但因為變長參數模板不受支持,導致不得不重復書寫7、8個模板類,但最終也只能支持7、8個參數的 function object。C++11中最終為我們帶來了強大的變長 參數模板功能,這些問題也隨之迎刃而解了。
可變參數模板(Variadic Template)故名思義,即可以接受任意數量參數的類/函數模板。 其聲明方式為
template<typename... Args> class VariadicTemplate;
當然non-type參數也是支持的比如 template<int... Args> class VariadicIntTemplate;
這裡Args代表0-N個類型參數,也就是說VariadicTemplate<>也是有效的模板實例。
C++11 翻新了省略號(...)操作符來支持對這種模板參數的訪問及使用。
類型展開
將所有模板參數展開在當前位置,用來調用其它模板、聲明函數指針類型
template<typename ...Args> int foo() { int (*p)(Args...) = bar<Args...>; p(1, 0.5); return bar<Args...>(1, 0.5); }
對於 foo<int, float> 實例,其 p 的類型為 int (*)(int, float) 而 return 語句將調用 bar<int, float>(1, 0.5)
此外還可以展開類型參數用來指定基類列表
template<typename... Args> class foo : Args...{ }; foo<bar, barz> f; // 該類繼承 bar, barz 兩個基類
Parameter Packs / Initialization Lists
通過省略號操作符可以聲明參數包,這些參數包可以在函數的參數位置展開。
template<typename ...Args> void foo(Args... args) { }
當使用 foo<int, float> 時,其有兩參數,一個整數,一個float,因此調用方法為 foo<int, float>(1, 0.1f);
此外還可以將參數包展開到構造函數的初始化列表
template<typename... Args> class foo : Args...{ public: foo(Args...args):Args(args)...{ } }; foo<bar, barz> f(bar(), barz());
sizeof... operator
要獲取模板變長參數的實參長度,使用 sizeof... 運算符,它也可以被用於 Parameter Packs
template<typename ... Args> size_t foo() { return sizeof...(Args); } template<typename ... Args> size_t fooz(Args ...args) { return sizeof...(args); }
以上就是變長參數的基本用法,但真正發揮作用,往往還要借助模板特化 (Template Specialization)
可變參數模板與模板特化
可變參數模板與普通模板一樣,可以進行特化,從而實現一些有意義的模式
template<typename ... Args> void output(Args ... args) { } template<typename Arg0, typename ... Args> void output(Arg0 arg0, Args ... args) { std::cout << arg0; output<Args...>(args...); } template<> void output() { } /*------------------------------*/ typedef decltype(std::cout) cout_type; auto endl = std::endl<cout_type::char_type, cout_type::traits_type>; output(1, 0.3, (void*)nullptr, "string", 'c', endl);
以上代碼輸出
10.30stringc