《C++ Templates》 技巧性基礎知識
關鍵字typename的相關問題:
首先需要注意的是當T存在內部型別的問題;
例如書上給出的示例:
template <typename T> void printcoll(const T& coll) { typename T::const_iterator pos; typename T::const_iterator end(coll.end()); for (pos = coll.begin(); pos != end; ++pos) { std::cout << *pos << ' '; } std::cout << std::endl; }
如上所示,注意下typename T::XXX的寫法,旨在使用T型別中的型別;
這裡給出的比較典型的例子就是關於STL庫中的問題,例如每一個類中都有一個迭代器型別進行宣告,如果想呼叫類似模板型別T中的型別,必須要在前面加上typename;
關於詳細的類內this以及.template構造詳見第九章;
成員模板的相關問題:
該章節主要想C++ primer一樣,以不同型別的賦值函式為例子,介紹了相關的典型的成員函式模板問題;
其中和C++ primer類似,也介紹了使用STL時自定義內部儲存單元的問題,即之前提到的預設模板形式:
template <typename T, typename CONT = std::vector<T>> class stack { private: CONT elems; };
但是這也存在一個問題,就是進行類新建的時候需要指定兩個模板引數stack<int,std::vector>s,這明顯是不符合STL一般化描述的,因此可以使用“模板的模板引數”來進行處理;
模板的模板引數:
C++templates在這裡相當不說人話,其實總結下來就是模板套裡面再套一個模板(但是注意,套得模板必須是類模板),並且套入得模板由其他第一層模板引數進行例項化;
所以受上述stack<int,std::vector>s得問題可以得到解決,如果可以通過int初始化std::vector型別,直接可以在使用時候指定一個引數就可以;
宣告形式如下:
k可以看到,在模板引數列表宣告處,第二個是一個新的模板,裡面的模板引數由類結構體內通過T顯示指出;
也就是ELEM直接就是T型別;
這裡注意一點,書上提到,如果不指定預設allocator為ELEM型別,會導致分配空間無法進行;
本質原因是標準庫中std::deque模板中第二個引數是allocator,為預設值,如果不進行指定的話會導致deque得allocator和CONT得型別無法匹配,所以需要顯式指定;
所以書上也提到了至關重要的一點:一定要考慮模板實參得預設模板引數匹配問題!
而對於後續得使用途中,如果沒有顯式得使用到ELEM等引數,可以直接留空寫typename即可;
程式碼如下所示:
template <typename T, template <typename ELEM, typename = std::allocator<ELEM> >class CONT = std::deque> class Stack { private: CONT<T> elems; public: void push(const T&); void pop(); T top() const; bool empty() const { return elems.empty(); } template <typename T2, template <typename ELEM2, typename = std::allocator<ELEM2> >class CONT2> Stack<T, CONT>& operator=(const Stack<T2, CONT2>&); }; template <typename T, template <typename, typename> class CONT> void Stack<T, CONT>::push(const T& elem) { elems.push_back(elem); } template <typename T, template<typename, typename> class CONT> template <typename T2, template <typename, typename> class CONT2>
Stack<T, CONT>& Stack<T, CONT>::operator=(const Stack<T2, CONT2>& op2) { if ((void*)this == (void*)&op2) { return *this; } Stack<T2, CONT2> tmp(op2); elems.clear(); while (!tmp.empty()) { elems.push_front(tmp.top()); tmp.pop(); } return *this; }
其中可以注意一下相關的寫法問題,即不顯式使用的引數沒有必要寫;
零初始化問題:
零初始化問題主要是針對於模板初始化的相關問題,避免模板初始化的不安全問題;
尤其針對於內建元素,例如T為int等問題;
template <typename T> class myclass { private: T x; public: myclass():x(){} };
對於模板中的未知型別得元素初始化呼叫x(),可以直接安全初始化,內建元素直接初始化為0;
字串問題:
這裡舉例了字串對比問題,也就是引用和非引用模板傳遞實參問題;
#include<iostream> #include<stdlib.h> #include<string> using namespace std; template <typename T> inline const T& rmax(const T& a, const T& b) { return a < b ? b : a; } int main() { //string a = "1239"; //string b = "456"; cout << rmax("1239", "456") << endl; system("pause"); return 0; }
會出現上述資訊,但是如果直接通過a,b字串來呼叫,並不會出現問題;
本質原因:引用和非引用字串得傳遞型別不同
對於const string& 傳遞的是char[n]陣列而,所以當陣列元素個數不一樣時,就會出現T模板引數不匹配的問題;
對於string形參,傳遞的是const char*指標,所以可以型別匹配;
一般的情況是顯式指定並且使用過載,但是還是建議使用std::string形式,因為有可能出現比較的是指標地址大小的情況;