我们在这篇文章来说说typename关键字吧。这个关键字是在c++的标准化过程中引入的,目的在于说明template中的某个表示符号是一个类型,而不是其他的东西,看下面的代码:
template <typename T> class MyClass { typename T::SubType * ptr; … };
第一个typename我就不多说了,大家都知道,我来说一下第二个。他的意思是说T::SubType是class T内部定义的一个类型,从而ptr是一个指向【T:SubType类型】的指针。
如果在上面的代码中,没有第二个typename关键字的话,编译器会以为SubType是class Type的一个static成员。于是会被编译器理解为一个具体的东西,从而导致T::SubType * ptr 所表达的意思是两个数进行相乘。
关于这个知识的应用比较多的是STL中,比如:
#include <iostream> // print elements of an STL container template <typename T> void printcoll (T const& coll) { typename T::const_iterator pos; // iterator to iterate over coll typename T::const_iterator end(coll.end()); // end position for (pos=coll.begin(); pos!=end; ++pos) { std::cout << *pos << ' '; } std::cout << std::endl; }
下面我们来看看另外一个有意思的问题。那就是”.template”,大家仔细查看下面的代码:
template<int N> void printBitset (std::bitset<N> const& bs) { std::cout << bs.template to_string<char,char_traits<char>, allocator<char> >(); }
大家注意到没有,。这个例子中的“.template”比较怪,但是如果没有他的话,那么编译器无法知道后面的“<”是模板参数的开始。而不是一个小于号。注意,只有当位于 点号之前的物件取决于某一个template parameter的时候,这种情况才会发生,以上的例子中,bs受控于N。也就是显式指明模板函数调用。可以理解为指名点姓的调用模板成员函数而非普通的。
结论是“.template”或者”->template”记号只能在templates中使用。而且他们必须紧跟在于template parameter相关的某个物件。
其实这里关于这个”.template”,笔者也没有搞懂,不知道哪位大牛搞懂了,麻烦指教一下,谢谢了。
下面我们来看看成员模板:
先看看下面的例子:
Stack<int> intStack1, intStack2; // stacks for ints Stack<float> floatStack; // stack for floats … intStack1 = intStack2; // OK: stacks have same type floatStack = intStack1; // ERROR: stacks have different types
默认的赋值运算要求左右两边拥有相等的类型。但是如果把赋值运算定义为一个模板,就可以使得类型不同,但是元素可以隐式转换,比如:
template <typename T> class Stack { private: std::deque<T> elems; // elements 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(); } // assign stack of elements of type T2 template <typename T2> Stack<T>& operator= (Stack<T2> const&); };
我们来实现它看看:关键代码如下:
template <typename T> template <typename T2> Stack<T>& Stack<T>::operator= (Stack<T2> const& op2) { if ((void*)this == (void*)&op2) { // assignment to itself? return *this; } Stack<T2> tmp(op2); // create a copy of the assigned stack elems.clear(); // remove existing elements while (!tmp.empty()) { // copy all elements elems.push_front(tmp.top()); tmp.pop(); } return *this; }
下面我们测试一下啊:
Stack<int> intStack; // stack for ints Stack<float> floatStack; // stack for floats … floatStack = intStack; // OK: stacks have different types, // but int converts to float
当然这个运算并不改变stack和其元素的类型,赋值完成转换,floatstack返回的任然是float。但是这个技术并不是说任何两个类型都能进行赋值,比如下面的代码就不行:
Stack<std::string> stringStack; // stack of ints Stack<float> floatStack; // stack of floats … floatStack = stringStack; // ERROR: std::string doesn't convert to float
你知道为什么了吗?
当然,和以前一样,你也可以在将内部容器的类型也参数化:
template <typename T, typename CONT = std::deque<T> > class Stack { private: CONT elems; // elements 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(); } // assign stack of elements of type T2 template <typename T2, typename CONT2> Stack<T,CONT>& operator= (Stack<T2,CONT2> const&); };
此时的赋值运算实现代码为:
template <typename T, typename CONT> template <typename T2, typename CONT2> Stack<T,CONT>& Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2) { if ((void*)this == (void*)&op2) { // assignment to itself? return *this; } Stack<T2> tmp(op2); // create a copy of the assigned stack elems.clear(); // remove existing elements while (!tmp.empty()) { // copy all elements elems.push_front(tmp.top()); tmp.pop(); } return *this; }