// impletement strcmp-like generic compare function // returns 0 if the values are equal, 1 if v1 is larger, -1 if v1 is smaller template <typename T> intcompare(const T &v1, const T &v2) { if (v1 < v2) return-1; if (v2 < v1) return1; return0; }
// ok: inline specifier follows template parameter list template <typename T> inline T min(const T&, const T&); // error: incorrect placement of inline specifier inlinetemplate <typename T> T min(const T&, const T&);
### 定义类模板 ###
#### 定义 ####
1 2 3 4 5 6 7 8 9 10 11
template <classType> classQueue { public: Queue (); // default constructor Type &front(); // return element from head of Queue const Type &front()const; voidpush(const Type &); // add element to back of Queue voidpop(); // remove element from head of Queue boolempty()const; // true if no elements in the Queue private: // ... };
类模板也是模板,因此必须以关键字 template 开头,后接模板形参表。Queue 模板接受一个名为 Type 的模板类型形参。
#### 使用 ####
与调用函数模板形成对比,使用类模板时,必须为模板形参显式指定实参:
1 2 3
Queue<int> qi; // Queue that holds ints Queue< vector<double> > qc; // Queue that holds vectors of doubles Queue<string> qs; // Queue that holds strings
### 模板类型形参 ###
类型形参由关键字 class 或 typename 后接说明符构成。在模板形参表中,这两个关键字具有相同的含义,都指出后面所接的名字表示一个类型。
模板类型形参可作为类型说明符在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同。
1 2 3 4 5 6 7 8
// ok: same type used for the return type and both parameters template <classT> Tcalc (constT& a, constT& b) { // ok: tmp will have same type as the parameters & return type T tmp = a; // ... return tmp; }
#### typename 与 class 的区别 ####
在函数模板形参表中,关键字 typename 和 class 具有相同含义,可以互换使用,两个关键字都可以在同一模板形参表中使用:
1 2
// ok: no distinctionparameter list between typename and class in template template <typename T, classU> calc (constT&, constU&);
使用关键字 typename 代替关键字 class 指定模板类型形参也许更为直观,毕竟,可以使用内置类型(非类类型)作为实际的类型形参,而且,typename更清楚地指明后面的名字是一个类型名。但是,关键字 typename 是作为标准 C++ 的组成部分加入到 C++ 中的,因此旧的程序更有可能只用关键字 class。
#### 定义类型成员 ####
除了定义数据成员或函数成员之外,类还可以定义类型成员。例如,标准库的容器类定义了不同的类型,如 size_type,使我们能够以独立于机器的方式使用容器。如果要在函数模板内部使用这样的类型,必须告诉编译器我们正在使用的名字指的是一个类型。必须显式地这样做,因为编译器(以及程序的读者)不能通过检查得知,由类型形参定义的名字何时是一个类型何时是一个值。例如,考虑下面的函数:
1 2 3 4 5 6
template <classParm, classU> Parmfcn(Parm* array, Uvalue) { Parm::size_type * p; // IfParm::size_typeis a type, then a declaration // If Parm::size_type is an object, then multiplication }
// initialize elements of an array to zero template <classT, size_tN> voidarray_init(T (&parm)[N]) { for (size_t i = 0; i != N; ++i) { parm[i] = 0; } }
template <typename T> T fobj(T, T); // arguments are copied template <typename T> T fref(const T&, const T&); // reference arguments strings1("a value"); conststrings2("another value"); fobj(s1, s2); // ok: calls f(string, string), const is ignored fref(s1, s2); // ok: non const object s1 converted to const reference int a[10], b[42]; fobj(a, b); // ok: calls f(int*, int*) fref(a, b); // error: array types don't match; arguments aren't converted to pointers
应用于非模板实参的常规转换
用普通类型定义的形参可以使用常规转换。
1 2 3 4 5 6 7 8 9
template <classType>{Type sum(const Type &op1, int op2) return op1 + op2; }
double d = 3.14; string s1("hiya"), s2(" world"); sum(1024, d); // ok: instantiates sum(int, int), converts d to int sum(1.4, d); // ok: instantiates sum(double, int), converts d to int sum(s1, s2); // error: s2 cannot be converted to int