代码改变世界

C++ template 学习归纳总结4

2012-03-08 10:57  Rollen Holt  阅读(526)  评论(0编辑  收藏  举报

我们在这篇文章来说说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; 
}