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

浙公网安备 33010602011771号