Templat 模板(整理)

temlate模板

函数模板的声明

>创建一个通用的函数, 支持多种不同的形参, 避免重载函数的函数体重复设计, 把函数使用的数据类型作为参数;

template<typename 数据类型参数标识符> <返回类型><函数名>(参数表) {    函数体   }

>template是定义模板函数的关键字; typename(或class)是声明数据类型参数标识符的关键字; 用以说明它后面的标识符是数据类型标识符;

>函数模板声明了一个函数的描述, 不是一个可以直接执行的函数, 只有根据实际情况用实参的数据类型代替类型参数标识符后才能产生真正的函数;

>关键字typename可以用class代替, 数据类型标识符可以使用所有C++数据类型;

函数模板的生成

>函数模板的类型参数标识符是一个类型形参, 在使用时要将形参实例化为确定的数据类型, 实例化的的参数称为模板实参, 函数称为模板函数;

1) 函数模板允许使用多个类型参数, 但在定义部分每个形参前必须有关键字typename或class;

template<class 数据类型参数标识符1,…,class 数据类型参数标识符n> <返回类型><函数名>(参数表) {     函数体   }

2)在template语句与函数模板定义语句<返回类型>之间不允许有别的语句;

3)模板函数类似于重载函数, 区别: 函数重载时, 每个函数体内可以执行不同的动作; 但同一个函数模板实例化后的模板函数都必须执行相同的动作;

函数模板异常处理

>函数模板中的模板形参可实例化成各种类型, 但当实例化模板形参的各模板实参之间不完全一致时, 会出现错误;

template <typename T>
void min(T &x, T &y)
{  return(x<y)?x:y;  }
void func(int i, char j)
{
    min(i, i);
    min(j, j);
    min(i, j);
    min(j, i);
}

错误 在调用时, 编译器按最先遇到的实参的类型隐含地生成一个模板函数, 并用它对所有模板函数进行一致性检查; 例如对语句 min(i, j); 先遇到的实参i是整型的 编译器就将模板形参解释为整型, 此后出现的模板实参j不能解释为整型而产生错误, 此时没有隐含的类型转换功能;

>C++中, 函数模板与同名的非模板函数重载时, 应遵循下列调用原则: 
-寻找一个参数完全匹配的函数, 若找到就调用它; 若参数完全匹配的函数多于一个, 则这个调用是一个错误的调用.
-寻找一个函数模板, 若找到就将其实例化生成一个匹配的模板函数并调用它.
-若上面两条都失败, 则使用函数重载的方法, 通过类型转换产生参数匹配, 若找到就调用它.
-若上面三条都失败, 还没有找都匹配的函数, 则这个调用是一个错误的调用.

Note 绝大多数C++编译器不支持将模版类/模版函数的声明与实现分开.  全部一起写在头文件中, 不要分开写到两个文件中;

<refer to> http://blog.csdn.net/beyondhaven/article/details/4204345

---End---


模板偏化和特偏化

模板的定义

1 类模板

>定义一个栈的类模板, 可以用来容纳不同的数据类型;

template <class T>
class stack {
private:
  list* top;
public:
  stack();
  stack(const stack&);
  ~stack();
  void push(T&);
  T& pop();
  //…
};

>template告诉编译器这是模板, <class T>指明模板参数, 可以有一个或多个, 具体实现由用户指定, 关键字class可以用typename代替;

>类模板的使用除了要在声明时指明参数模板, 其余与普通类相同; e.g.

stack<int> int_stack;
stack<char> ch_stack;
stack<string> str_stack;
int_stack.push(10);
ch_stack.push(‘z’);
str_stack.push(“c++”);

2 函数模板

>定义一个max函数返回同一类型(这种类型允许比较)两个值的最大值;

template<class T>
T mymax(const T& t1,const T& t2)
{ return t1 < t2 ? t2 : t1; }

>template <class T>的意义与类模板定义中相同;

>模板函数的使用和普通函数使用相同, 因为模板函数的参数可以从其传入参数中解析出来; e.g.

int highest = mymax(5,10); //解析出模板函数参数为int
char c = mymax(‘a’, ’z’); //解析出模板函数的参数为char

3 模板的特化

>给模板中的所有模板参数一个具体的类;

1)类模板特化

>有时为了需要, 针对特定的类型, 需要对模板进行特化, 即特殊处理; e.g. stack模板针对bool类型, 因为bool只需要一个二进制位就可以对其进行存储, 使用一个字或者一个字节都是浪费存储空间;

template <class T>
class stack {};
template < > //告诉编译器这是一个特化的模板
class stack<bool> { //…// };

2)函数模板特化

int highest = mymax(5,10);
char c = mymax(‘a’, ’z’);
const char* p1 = “hello”;
const char* p2 = “world”;
const char* p = mymax(p1,p2);

>前面两个mymax正确, 第三个不能返回正确结果, 因为此时mymax直接比较两个指针, 而不是指针指向的内容;

>当参数类型为const char*时的特化:

template <class T>
T mymax(const T t1, const T t2)
{
   return t1 < t2 ? t2 : t1;
}
template <>
const char* mymax(const char* t1,const char* t2)
{
   return (strcmp(t1,t2) < 0) ? t2 : t1;
}

4 模板的偏特化

>给模板中的部分模板参数以具体的类, 剩余的模板参数仍然是一原来的泛化定义;

>需要根据模板的某些但不是全部的参数进行特化;

>当你实例化一个模板时, 编译器会把目前存在的偏特化模板和全特化模板做比较, 找出其中最合适, 最匹配的实现;

1)类模板的偏特化

>stl中的类vector定义:

template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};

这个例子中, 一个参数被绑定到bool类型, 另一个参数仍未绑定, 需要由用户指定;

2)函数模板的偏特化

Note 严格来说,函数模板不支持特偏化, 但由于可以对函数进行重载, 所以可以达到类似于类模板偏特化的效果;

a) template <class T> void f(T); 对a)进行重载: b) template <class T> void f(T*);
如果a)称为基模板, 那b)称为对基模板a)的重载, 而非对a)的特偏化;

Note 类模板class template能被全特化fully specialized或偏特化partially specialized, 函数模板只能被全特化; 由于函数模板可以重载, 可以通过重载实现偏特化的功能;

5 模板特化时的匹配规则

1)类模板的匹配规则

>最特化的优于次特化的, 模板参数最精确匹配的具有最高的优先权;

template <class T> class vector{//…//}; // (a)  普通型
template <class T> class vector<T*>{//…//};  // (b) 对指针类型特化
template <>   class vector <void*>{//…//};  // (c) 对void*进行特化

每个类型都可以用作普通型(a)的参数, 但只有指针类型才能用作(b)的参数. 只有void*才能作为(c)的参数;

2)函数模板的匹配规则

>非模板函数具有最高的优先权; 如果不存在匹配的非模板函数, 那么最匹配的和最特化的函数具有高优先权;

template <class T> void f(T);  // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*);  // (f)
template <> void f<int> (int) ; // (g)
void f(double);  // (h)
bool b = false;
int i = 1;
double d = 2.0;
f(b); // 以 T = bool 调用 (d)
f(42,d) // 以 T = int 调用(e)
f(&i) ; // 以 T = int* 调用(f)
f(d);  //  调用(h)

<refer to> http://blog.csdn.net/zhang810413/article/details/1948603

---End---


于函数模板特化, 有个有趣的例子:

template <class T> void fuc(T t) {cout<<"1"<<endl;}
template <class T> void fuc(T* pt) {cout<<"3"<<endl;}
template <> void fuc(int* pi) {cout<<"2"<<endl;}
//---
    int *pi = new int(1);
    fuc(pi);

输出是什么?   结果是2;

template <class T> void fuc(T t) {cout<<"1"<<endl;}
template <> void fuc(int* pi) {cout<<"2"<<endl;}
template <class T> void fuc(T* pt) {cout<<"3"<<endl;}

如果把第二第三行交换, 结果就变成了3;

Note: Specializations don't overload,only the base templates overload;
重载解析只选择基模板(或者非模板)函数, 当编译器选择了那个合适的基模板函数, 才会继续往下找合适的特化版本; 

>原作者给出了一个方案: 函数模板调用类模板成员函数, 只特化类模板, 绕过了函数模板的这个陷阱:

//base template class,
 template <class T>
  struct FuncImpl {
     //users, go ahead and specialize this
     static int apply(const T & t) {
         return 0 ;
      }
  };
  //partial specialazation for int
  template <>
  struct FuncImpl<int*>  {
      static int apply(int* t) {
          return 1 ;
       }
   };
 //partial specialazation for T*
 template <class T>
     struct FuncImpl<T *>  {
     static int apply(T * t) {
         return 2 ;
      }
  };
 //users, don't touch this!
 template <class T>
 int func(const T & t) {
     return FuncImpl<T> ::apply(t);
  }
//---
   int i = 10;
   cout<<func('c'); //r = 0
   cout<<func(8); //r = 1
   cout<<func(&i); //r = 2

这是个很安全的方法, 但是如果你比较懒, 觉得这个方案很费代码的话, 就要小心使用函数模板的特化: 先选择适合的重载的模板函数, 再去扩展它的特化版本;

<refer to> http://www.cppblog.com/ant/archive/2007/08/30/31201.html

---End---

你可能感兴趣的:(Templat 模板(整理))