关键字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形式,因为有可能出现比较的是指针地址大小的情况;