模板

函数模板

1.定义函数模板:模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔,模板形参表不能为空。比如:

1 template <typename T>
2      int compare(const T &v1, const T &v2)
3      {
4          if (v1 < v2) return -1;
5          if (v2 < v1) return 1;
6          return 0;
7      }

模板形参表很像函数形参表,函数形参表定义了特定类型的局部变量但并不初始化那些变量,在运行时再提供实参来初始化形参。模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。非类型形参跟在类型说明符之后声明,类型形参跟在关键字 classtypename 之后定义,例如,typenameT 是名为 T 的类型形参。

2.使用函数模板时,编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例。实质上,编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数。编译器承担了为我们使用的每种类型而编写函数的单调工作。比如:

 1      int main ()
 2      {
 3          // T is int;
 4          // compiler instantiates int compare(const int&, const int&)
 5          cout << compare(1, 0) << endl;
 6          // T is string;
 7          // compiler instantiates int compare(const string&, const string&)
 8          string s1 = "hi", s2 = "world";
 9          cout << compare(s1, s2) << endl;
10          return 0;
11      }

编译器将实例化 compare 的两个不同版本,编译器将用 int 代替 T 创建第一个版本,并用 string 代替 T 创建第二个版本。

3.inline 函数模板

函数模板可以用与非模板函数一样的方式声明为 inline说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前

1  // ok: inline specifier follows template parameter list
2      template <typename T> inline T min(const T&, const T&);
3      // error: incorrect placement of inline specifier
4      inline template <typename T> T min(const T&, const T&);

4.模板实参推断

要确定应该实例化哪个函数,编译器会查看每个实参。如果相应形参声明为类型形参的类型,则编译器从实参的类型推断形参的类型。在 compare 的例子中,两个实参有同样的模板类型,都是用类型形参 T 声明的。

第一个调用 compare(1, 0) 中,实参为 int 类型;第二个调用 compare(3.14, 2.7) 中,实参为 double 类型。从函数实参确定模板实参的类型和值的过程叫做模板实参推断

模板类型形参可以用作一个以上函数形参的类型。在这种情况下,模板类型推断必须为每个对应的函数实参产生相同的模板实参类型。如果推断的类型不匹配,则调用将会出错:

 1      template <typename T>
 2      int compare(const T& v1, const T& v2)
 3      {
 4          if (v1 < v2) return -1;
 5          if (v2 < v1) return 1;
 6          return 0;
 7      }
 8      int main()
 9      {
10          short si;
11          // error: cannot instantiate compare(short, int)
12          // must be: compare(short, short) or
13          // compare(int, int)
14          compare(si, 1024);
15          return 0;
16      }

如果想要允许实参的常规转换,则函数必须用两个类型形参来定义:

1      template <typename A, typename B>
2      int compare(const A& v1, const B& v2)
3      {
4          if (v1 < v2) return -1;
5          if (v2 < v1) return 1;
6          return 0;
7      }

这样上面的调用就没有问题了,用于比较的类型必须支持<比较。如果是类类型该类必须实现重载<。

5.类型形参的实参的受限转换

一般而论,不会转换实参以匹配已有的实例化,相反,会产生新的实例。除了产生新的实例化之外,编译器只会执行两种转换:

(1)const 转换:接受 const 引用或 const 指针的函数可以分别用非 const 对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型实参都忽略 const,即,无论传递 const 或非 const 对象给接受非引用类型的函数,都使用相同的实例化。

(2)数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。

 1 template <typename T> T fobj(T, T); // arguments are copied
 2      template <typename T>
 3      T fref(const T&, const T&);       // reference arguments
 4      string s1("a value");
 5      const string s2("another value");
 6      fobj(s1, s2);     // ok: calls f(string, string), const is ignored
 7      fref(s1, s2);     // ok: non const object s1 converted to const reference
 8      int a[10], b[42];
 9      fobj(a, b); // ok: calls f(int*, int*)
10      fref(a, b); // error: array types don't match; arguments aren't converted to pointers

类模板

1.定义类模板

类模板也是模板,因此必须以关键字 template 开头,后接模板形参表。Queue 模板接受一个名为 Type 的模板类型形参。比如:

 1      template <class Type> class Queue {
 2      public:
 3          // empty Queue
 4          Queue(): head(0), tail(0) { }
 5          // copy control to manage pointers to QueueItems in the Queue
 6          Queue(const Queue &Q): head(0), tail(0)
 7                                        { copy_elems(Q); }
 8          Queue& operator=(const Queue&);
 9          ~Queue() { destroy(); }
10               // return element from head of Queue
11          // unchecked operation: front on an empty Queue is undefined
12          Type& front()             { return head->item; }
13          const Type &front() const { return head->item; }
14          void push(const Type &);       // add element to back of Queue
15          void pop ();                    // remove element from head of Queue
16          bool empty () const {           // true if no elements in the Queue
17              return head == 0;
18          }
19      private:
20          QueueItem<Type> *head;         // pointer to first element in Queue
21          QueueItem<Type> *tail;         // pointer to last element in Queue
22          // utility functions used by copy constructor, assignment, and destructor
23          void destroy();                // delete all the elements
24          void copy_elems(const Queue&); // copy elements from parameter
25      };

除了模板形参表外,类模板的定义看起来与任意其他类问相似。类模板可以定义数据成员、函数成员和类型成员,也可以使用访问标号控制对成员的访问,还可以定义构造函数和析构函数等等。在类和类成员的定义中,可以使用模板形参作为类型或值的占位符,在使用类时再提供那些类型或值。

2.使用类模板

与调用函数模板形成对比,使用类模板时,必须为模板形参显式指定实参:

1      Queue<int> qi;                 // Queue that holds ints
2      Queue< vector<double> > qc;    // Queue that holds vectors of doubles
3      Queue<string> qs;              // Queue that holds strings

3.类模板函数实现方式:

template <class Type> returnType  classTemplate::(arg)比如:

1      template <class Type> void Queue<Type>::destroy()
2      {
3          while (!empty())
4              pop();
5      }

4.何时实例化类和成员

类模板的成员函数只有为程序所用才进行实例化。如果某函数从未使用,则不会实例化该成员函数。这一行为意味着,用于实例化模板的类型只需满足实际使用的操作的要求。定义模板类型的对象时,该定义导致实例化类模板。定义对象也会实例化用于初始化该对象的任一构造函数,以及该构造函数调用的任意成员,比如:

1     // instantiates Queue<int> class and Queue<int>::Queue()
2      Queue<string> qs;
3      qs.push("hello"); // instantiates Queue<int>::push

 

5. 类模板中的友元声明

在类模板中可以出现三种友元声明,每一种都声明了与一个或多个实体友元关系:

(1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数。

(2)类模板或函数模板的友元声明,授予对友元所有实例的访问权。

(3)只授予对类模板或函数模板的特定实例的访问权的友元声明。

6.类模板的 static 成员

(1)类模板static成员,为实例化的类共享

(2)类模板static成员使用的时候必须实例化

(3)像使用任意其他 static 数据成员一样,必须在类外部出现数据成员的定义。在类模板含有 static 成员的情况下,成员定义必须指出它是类模板的成员:

    template <class T>
     size_t Foo<T>::ctr = 0; // define and initialize ctr

7. 模板特化

1.为什么要模板特化:某些情况下,通用模板定义对于某个类型可能是完全错误的,通用模板定义也许不能编译或者做错误的事情;另外一些情况下,可以利用关于类型的一些特殊知识,编写比从模板实例化来的函数更有效率的函数。

2.函数模板特化

template<>

returnType funName<teplate agruments>(arguments)

{

    //something should do

}

比如:

    template <>
     int compare<const char*>(const char* const &v1,
                              const char* const &v2)
     {
         return strcmp(v1, v2);
     }

特化的声明必须与对应的模板相匹配。在这个例子中,模板有一个类型形参和两个函数形参,函数形参是类型形参的 const 引用,在这里,将类型形参固定为 const char*,因此,函数形参是 const char*const 引用。

 3.类模板特化

(1)类模板特化应该与它所特化的模板定义相同的接口,否则当用户试图使用未定义的成员时会感到奇怪。

 (2)在类特化外部定义成员时,成员之前不能加 template<> 标记。

 

 

 

posted @ 2013-10-07 20:48  觉远大师  阅读(246)  评论(0编辑  收藏  举报