概念
一個模板的引數是模板型別。
舉例
在c++11-17 模板核心知識(二)—— 類别範本 中,如果我們想要允許指定儲存Stack元素的容器,是這麼做的:
template <typename T, typename Cont = std::vector<T>>
class Stack {
private:
Cont elems; // elements
......
};
使用:
Stack<double,std::deque<double>> dblStack;
但是這樣的缺點是需要指定元素型別兩次,然而這兩個型別是一樣的。
使用模板的模板引數(Template Template Parameters),允許我們在宣告Stack類别範本的時候只指定容器的型別而不去指定容器中
元素的型別。例如:
template <typename T, template <typename Elem> class Cont = std::deque>
class Stack {
private:
Cont<T> elems; // elements
public:
void push(T const &); // push element
void pop(); // pop element
T const &top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}
...
};
使用:
Stack<int, std::vector> vStack; // integer stack that uses a vector
與第一種方式的區別是:第二個模板引數是一個類别範本:
template<typename Elem> class Cont
預設值從std::deque<T>
改為了std::deque
.
在C++17之後,模板的模板引數中的class也可以使用typename,但是不可以使用struct和union:
template <typename T,
template <typename Elem> typename Cont = std::deque>
class Stack { // ERROR before C++17
...
};
......
template<template<typename X> class C> // OK
void f(C<int>* p);
template<template<typename X> struct C> // ERROR: struct not valid here
void f(C<int>* p);
template<template<typename X> union C> // ERROR: union not valid here
void f(C<int>* p);
當然,由於模板的模板引數中的Elem沒有用到,可以省略:
template <typename T, template <typename> class Cont = std::deque>
class Stack {
...
};
另外注意一點,模板的模板引數中的模板引數,只能和模板的模板引數配合用。有點饒,舉個例子:
template<template<typename T, T*> class Buf> // OK
class Lexer {
static T* storage; // ERROR: a template template parameter cannot be used here
...
};
模板的模板引數的引數匹配 Template Template Argument Matching
大家可以嘗試自己編譯一下上面的程式碼,可能會出現下列問題:
error: template template argument has different template parameters than its corresponding template template parameter
template <typename T, template <typename Elem> class Cont = std::deque>
...
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/deque:1197:1: note: too many template parameters in template template argument
template <class _Tp, class _Allocator /*= allocator<_Tp>*/>
意思是std::deque
和Cont
不匹配。標準庫的std::deque
有兩個引數,還有一個預設引數Allocator :
template <class _Tp, class _Allocator = allocator<_Tp> > class _LIBCPP_TEMPLATE_VIS deque;
解決辦法一
將Cont和std::deque的引數匹配即可:
template <typename T,
template <typename Elem, typename Alloc = std::allocator<Elem>>
class Cont = std::deque>
class Stack {
......
};
這裡的Alloc沒有用到,同樣可以省略。
成員函式定義舉例:
template<typename T, template<typename,typename> class Cont>
void Stack<T,Cont>::push (T const& elem) {
elems.push_back(elem); // append copy of passed elem
}
解決辦法二
利用c++11-17 模板核心知識(四)—— 可變引數模板 Variadic Template
template <typename T,
template <typename......>
class Cont = std::deque>
class Stack {
......
};
但是,這點對於std::array
無效,因為std::array的第二個引數是非型別模板引數 Nontype Template Parameters:
// template<typename T, size_t N>
// class array;
假如使用 Stack<int,std::array> s;
,那麼編譯器會報錯:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/array:126:29: note: template parameter has a different kind in template argument
template <class _Tp, size_t _Size>
^
main.cc:22:33: note: previous template template parameter is here
template <typename... Elem>
^
(完)
朋友們可以關注下我的公眾號,獲得最及時的更新: