1. To access a type name that depends on a template parameter, you have to qualify(修改,修饰) the name with a leading typename
 // print elements of an STL container
// print elements of an STL container
 template <typename T>
template <typename T>
 void printcoll (T const& coll)
void printcoll (T const& coll)


 {
{
 typename T::const_iterator pos;  // iterator to iterate over coll
    typename T::const_iterator pos;  // iterator to iterate over coll
 typename T::const_iterator end(coll.end());  // end position
    typename T::const_iterator end(coll.end());  // end position


 for (pos=coll.begin(); pos!=end; ++pos)
    for (pos=coll.begin(); pos!=end; ++pos)  {
{
 std::cout << *pos << ' ';
        std::cout << *pos << ' ';
 }
    }
 std::cout << std::endl;
    std::cout << std::endl;
 }
}2. Nested classes and member functions can also be templates. One application is the ability to implement generic operations with internal type conversions. However, type checking still occurs.

 class Stack
class Stack  {
{
 private:
  private:
 std::deque<T> elems;   // elements
    std::deque<T> elems;   // elements

 public:
  public:
 void push(T const&);   // push element
    void push(T const&);   // push element
 void pop();            // pop element
    void pop();            // pop element
 T top() const;         // return top element
    T top() const;         // return top element

 bool empty() const
    bool empty() const  {   // return whether the stack is empty
{   // return whether the stack is empty
 return elems.empty();
        return elems.empty();
 }
    }

 // assign stack of elements of type T2
    // assign stack of elements of type T2
 template <typename T2>
    template <typename T2>
 Stack<T>& operator= (Stack<T2> const&);
    Stack<T>& operator= (Stack<T2> const&);
 };
};

 template <typename T>
template <typename T>
 template <typename T2>
 template <typename T2>
 Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)


 {
{

 if ((void*)this == (void*)&op2)
    if ((void*)this == (void*)&op2)  {    // assignment to itself?
{    // assignment to itself?
 return *this;
        return *this;
 }
    }

 Stack<T2> tmp(op2);              // create a copy of the assigned stack
    Stack<T2> tmp(op2);              // create a copy of the assigned stack

 elems.clear();                   // remove existing elements
    elems.clear();                   // remove existing elements

 while (!tmp.empty())
    while (!tmp.empty())  {           // copy all elements
{           // copy all elements
 elems.push_front(tmp.top());
        elems.push_front(tmp.top());
 tmp.pop();
        tmp.pop();
 }
    }
 return *this;
    return *this;
 }
}

3. Template versions of assignment operators don't replace default assignment operators
4. You can also use class templates as template parameters, as so-called 
template template parameters
To use a different internal container for stacks, the application programmer has to specify the element type twice. Thus, to specify the type of the internal container, you have to pass the type of the container and the type of its elements again:
Stack<int,std::vector<int> > vStack; // integer stack that uses a vector
Using template template parameters allows you to declare the Stack class template by spcecifying the type of the container without respecifying the type of its elements:
stack<int, std::vector> vStack; // integer stack the uses a vector
To do this you must specify the second template parameter as a template template parameter.
 template <typename T,
template <typename T,
 template <typename ELEM> class CONT = std::deque >
          template <typename ELEM> class CONT = std::deque >

 class Stack
class Stack  {
{
 private:
  private:
 CONT<T> elems;         // elements
    CONT<T> elems;         // elements

 public:
  public:
 void push(T const&);   // push element
    void push(T const&);   // push element
 void pop();            // pop element
    void pop();            // pop element
 T top() const;         // return top element
    T top() const;         // return top element

 bool empty() const
    bool empty() const  {   // return whether the stack is empty
{   // return whether the stack is empty
 return elems.empty();
        return elems.empty();
 }
    }
 };
};The different is that the second template parameter is declare as being a class template:
template <typename ELEM> class CONT
5. Template template arguments must match exactly. Default template arguments of template template arguments are ignored
The problem in this example is that the std::deque template of the standard library has more than one parameter: the second parameter has a default value, but this is not considered when match std::deque to the CONT parameter.
We can rewrite te class declaration so that the CONT parameter expects containers with two template parameters:
 template <typename T,
template <typename T,
 template <typename ELEM,
          template <typename ELEM, 
 typename ALLOC= std::allocator<ELEM> >
                    typename ALLOC= std::allocator<ELEM> >
 class CONT = std::deque>
                    class CONT = std::deque>

 class Stack
class Stack  {
{
 private:
  private:
 CONT<T> elems;         // elements
    CONT<T> elems;         // elements


 };
};

 6. By explicitly calling a default constructor, you can make sure that variables and members of templates are initialized by a default value even if they are instantiated with a built-in type.
7.  For string literals there is an array-to-pointer conversion during argument deduction if and only if the parameter is not a reference
Passing string literal arguments for reference parameters of templates sometimes fails in a surprising way.
 // note: reference parameters
// note: reference parameters
 template <typename T>
template <typename T>
 inline T const& max (T const& a, T const& b)
inline T const& max (T const& a, T const& b)


 {
{
 return  a < b  ?  b : a;
    return  a < b  ?  b : a;
 }
}

 int main()
int main()


 {
{
 std::string s;
    std::string s;

 ::max("apple","peach");   // OK: same type
    ::max("apple","peach");   // OK: same type
 ::max("apple","tomato");  // ERROR: different types
    ::max("apple","tomato");  // ERROR: different types
 ::max("apple",s);         // ERROR: different types
    ::max("apple",s);         // ERROR: different types
 }
}The problem is that string literals have defferent array depending on their lengths.
However, if you declare nonreference parameters, you can substitute them with string literals of different size:
 // note: nonreference parameters
// note: nonreference parameters
 template <typename T>
template <typename T>
 inline T max (T a, T b)
inline T max (T a, T b)


 {
{
 return  a < b  ?  b : a;
    return  a < b  ?  b : a;
 }
}

 int main()
int main()


 {
{
 std::string s;
    std::string s;

 ::max("apple","peach");   // OK: same type
    ::max("apple","peach");   // OK: same type
 ::max("apple","tomato");  // OK: decays to same type
    ::max("apple","tomato");  // OK: decays to same type
 ::max("apple",s);         // ERROR: different types
    ::max("apple",s);         // ERROR: different types
 }
}The explanation for this behavior is that during argument deduction array-to-pointer conversion(often called decay) occurs only if the parameter does not have a reference type.
 template <typename T>
template <typename T>
 void ref (T const& x)
void ref (T const& x)


 {
{
 std::cout << "x in ref(T const&): "
    std::cout << "x in ref(T const&): "  
 << typeid(x).name() << '\n';
              << typeid(x).name() << '\n';
 }
}

 template <typename T>
template <typename T>
 void nonref (T x)
void nonref (T x)


 {
{
 std::cout << "x in nonref(T):     "
    std::cout << "x in nonref(T):     "
 << typeid(x).name() << '\n';
              << typeid(x).name() << '\n';
 }
}

 int main()
int main()


 {
{
 ref("hello");
    ref("hello");
 nonref("hello");
    nonref("hello");
 }
}the output might be as follows:
x in ref(T const&): char[6]
x in nonref(T): const char *