关键字typename:
引入关键字typename是为了说明:模板内部的标识符可以是一个类型:
template <typename T>
class Myclass {
typename T::SubType *ptr;
}
.template构造:
void printBitset (std::bitset<N> const& bs) {
std::cout<< bs.template to_string< char, char_traits<char>, allocator<char> >();
}
.template告诉编译器.template后面的小于号是模板实参列表的起始符号。
使用this->:
对于那些在基类中声明,并且依赖于模板参数的符号(函数或变量等),应该在它们前面使用this->或者Base<T>::。
如果希望完全避免不确定性,可以限定模板中所有的成员访问。
成员模板和模板的模板参数:
通常,栈之间只有在类型完全相同时才能相互赋值,其中类型指的是元素的类型,即使这两种元素的类型之间存在隐式类型转换。‘
函数模板不支持模板的模板参数。
不同类型间赋值的解决办法(并加入模板的模板参数的完整版):
- #include <deque>
- #include <stdexcept>
- #include <memory>
- template <typename T,
- template <typename ELEM, typename = std::allocator<ELEM> > class CONT = std::deque> //模板的模板参数,其中类的两个模板参数是为了匹配std::deque的两个模板实参缺省模板参数(元素类型和内存分配器allocator)
- class Stack {
- private:
- CONT<T> elems; // 由T来决定类模板中的元素的类型,实例化时可直接写为:Stack<int, std::vector> vStack
- public:
- void push(T const&); // push element
- void pop(); // pop element
- T top() const; // return top element
- bool empty() const { // return whether the stack is empty
- return elems.empty();
- }
- //在元素不同的栈之间相互赋值的重载operator=
- template<typename T2,
- template<typename ELEM2, typename = std::allocator<ELEM2> >class CONT2>
- Stack<T,CONT>& operator= (Stack<T2,CONT2> const&);
- };
- 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
- }
- template<typename T, template <typename,typename> class CONT>
- void Stack<T,CONT>::pop ()
- {
- if (elems.empty()) {
- throw std::out_of_range("Stack<>::pop(): empty stack");
- }
- elems.pop_back(); // remove last element
- }
- template <typename T, template <typename,typename> class CONT>
- T Stack<T,CONT>::top () const
- {
- if (elems.empty()) {
- throw std::out_of_range("Stack<>::top(): empty stack");
- }
- return elems.back(); // return copy of last element
- }
- //在元素不同的栈之间相互赋值的重载operator=
- template <typename T, template <typename,typename> class CONT>
- template <typename T2, template <typename,typename> class CONT2>
- Stack<T,CONT>& Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2)
- {
- if ((void*)this == (void*)&op2) { // 自身赋值检测
- return *this;
- }
- Stack<T2,CONT2> tmp(op2); // 创建一份赋值栈的拷贝,以便使用top()和pop()从该拷贝中获取元素。
- elems.clear(); // 移除现有元素
- while (!tmp.empty()) { // 拷贝所有元素
- elems.push_front(tmp.top());
- tmp.pop();
- }
- return *this;
- }
零初始化:
对于int、double或者指针等基本类型,应该显式地调用内建类型的缺省构造函数,并把缺省值设为0(或者false,对于bool类型而言)。比如调用int()我们就将获得缺省值0。
template <typename T>
void foo() {
T x = T() ;
}
对于类模板,需要定义一个缺省构造函数,通过一个初始化列表来初始化类模板的成员。
template <typename T>
class Myclass {
private:
T x;
public:
Myclass() : x()
...
};
使用字符串作为函数模板的实参:
如果把字符串传递给函数模板的引用参数时,由于长度的区别,长度不同的字符串属于不同的数组类型,可以通过传递给非引用类型的参数来解决这个问题,但对于非引用类型的参数,在实参演绎的过程中,会出现数组到指针的类型转换。
根据不同的情况,对此类问题的解决方法:
- 使用非引用参数,取代引用参数(然而,可能会导致无用的拷贝)
- 进行重载,编写接收引用参数和非引用参数的两个重载函数(然而,可能会导致二义性)
- 对具体类型进行重载(比如对std::string进行重载)
- 重载数组类型,如:
template <typename T, int N, int M>
T const* max(T const (&a)[N], T const (&b)[M]) {
return a < b ? b : a;
}
- 强制要求应用程序程序员使用显式类型转换。
小结:
- 如果要访问依赖于模板参数的类型名称,应该在类型名称前添加关键字typename。
- 嵌套类和成员函数也可以是模板。
- 类模板也可以作为模板参数,称之为模板的模板参数。
- 模板的模板实参必须精确地匹配。
- 通过显式调用缺省构造函数,可以确保模板的变量和成员都已经用一个缺省值完成初始化,这种方法对内建类型的变量和成员也适用。
- 对于字符串,在实参演绎过程中,当且仅当参数不是引用时,才会出现数组到指针的类型转换。