第十五天
函数模板
template 后接模板形参表
template <typename T>
int compare(const T &v1,const T &v2)
{
if(v1<v2) return -1;
if(v2<v1) return 1;
return 0;
}
函数形参报定义了特定类型的局部变量但并不初始化那些变量,在运行时来初始化形参。
template <typedef T> inline T min(const T&,const T&);
类模板
以关键字template开头,后接模板形参列表。
Queue模板接受一个名为Type的模板类型形参。
template <class Type> class Queue
{
public:
Queue();
Tyep &front();
const Type &front() const;
void push(const Type &);
void pop();
bool empty() const;
private:
//...
};
模板形参
给模板形参富裕的唯一含义是区别形参是类型形参还是非类型形参:
- 类型形参:该形参表示的是未知类型
- 非类型形参:是一个未知值
模板形参会遵循常规的名字屏蔽规则。
用作模板形参的名字不能在模板内部重用。并且同意模板形参的名字只能在同一模板形参表中使用一次。
模板类型形参前必须带上关键字class或typename。
类型成员
必须显式指定为类型。否则默认为数据成员。
template <class Parm,class U>
Parm fcn(Parm* array,U value>
{
typename Parm::size_type *p;
}
给用实例化fcn的类型早呢更加了一个指责:那些类型个必须具有名为size_type的成员,并且是一个类型。
非类型模板形参
template <class T,size_t N> void array_init(T (&parm)[N])
{
for(size_t i = 0;i!=N;++i)
{
parm[i] = 0;
}
}
array_init是一个含有一个类型模板形参和一个非类型模板形参的函数模板。
函数本身接受一个数组的引用形参。
对实参类型的要求尽可能少是很有益的。
- 模板的形参是const引用
- 函数中的测试只用<比较 可以减少对函数的要求
实例化
编译器用模板产生指定的类或函数的特定类型版本。产生版本的特定类型实例的过程称为实例化。
Queue<int> qi;
==
template <class Type> class Queue<int>
{
public:
Queue();
int &front();
const int &front() const;
void push(const int &);
void pop();
bool empty() const;
private:
//....
};
类型形参的实参的受限转换 typename的调用
如果要允许实参的常规转换,则函数必须用两个类型形参来定义。
short si;
template <typename T,typename R>
int compare(const T &v1,const T &v2)
{
if(v1<v2) return -1;
if(v2<v1) return 1;
return 0;
}
编译器只会执行两种转换:
- const转化:就收const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无需产生新的实例化。如果函数接受非引用类型,形参类型实参都忽略const。
- 数组或函数到指针的转换:如果模板不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当做指向其第一个元素的指针,函数实参当做指向函数类型的指针。
template<typename T> T fobj(T,T);
template<typename T) T fref(const T&,const T&);
string s1("a value");
const string s2 ("another value");
fobj(s1,s2);//const 忽略
fref(s1,s2);//s1转化成const
int a[10],b[42];
fobj(a,b);// f(int *,int *);
fref(a,b);//error 引用不能转化成指针
模板实参推断和函数指针
template<typename T> int compare(const T&,const T&);
int (*pf1)(const int &,const int&) = compare;
指针pf1,指向 接受两个const int& 类型形参并返回int值的函数。
函数模板的显式形参 覆盖模板实参推断机制
希望定义名为sum、接受两个不同类型实参的函数模板,希望返回类型足够大。
- 强制sum的调用者将较小的类型强制转换为希望用作结果使用的类型
int i;short s;
sum(stract_cast<int>(s),i);
- 引入第三个模板形参
template<class T1,class T2,class T3);
T1 sum(T2,T3);
- 显式模板实参
long val3= sum<long>(i,lng);
显式模板实参可以消除二义性
template<typename T> int compare(const T&,const T&);
void func(int(*)(const string&,const string&));
void func(int(*)(const int&,const int&));
func(compare<int>);
模板编译模型
将类定义和函数声明放在头文件中。而普通函数和类成员函数的定义放在源文件中。
编译模板:类定义和函数声明放在头文件中,函数定义和成员函数放在源文件中。
所有编译器支持“包含模型”,只有一些编译器支持“分别编译模型”。
包含编译模型
编译器必须看到所有模板的定义。 #include<>
分别编译模型
让编译器知道要记住给定模板定义。export
指明给定的定义可能会需要在其他文件中产生实例化。
template <class Type>
class QueueItem
{
QueueItem(const Type &t) : item(t), next(0) {}
Type item;
QueueItem *next;
};
template <class Type>
class Queue
{
//默认构造函数
Queue() : head(0), tail(0);
//复制构造函数 初始化head 和tail 并调用函数复制元素
Queue(const Queue &Q) : head(0), tail(0)
{
copy_elems(Q);
}
Queue &operator=(const Queue &);
~Queue() { destory(); }
Type &front() { return head->item; }
const Type &front() const { return head->item; }
void push(const Type &);
void pop();
bool empty() const
{
return head == 0;
}
private:
QueueItem<Type> *head;
QueueItem<Type> *tail;
void destory();
void copy_elems(const Queue &);
};
template <class Type>
void Queue<Type>::destory()
{
while (!empty)
{
pop();
}
}
template <class Type>
void Queue<Type>::pop()
{
QueueItem<Type> *p = head;
head = head->next;
delete p;
}
template <class Type>
void Queue<Type>::push(const Type &val)
{
//分配新的QueueItem对象 用传递的值初始化
QueueItem<Type> *pt = new QueueItem<Type>(val);
if (empty())
{
head = tail = pt;
}
else
{
tail->next = pt;
tail = pt;
}
}
template <class Type>
void Queue<Type>::copy_elems(const Queue &orig)
{
for (QueueItem<Type> *pt = orig.head; pt; pt = pt->next)
{
push(pt->item);
}
}
类模板中的友元声明
- 普通非模板类或函数的友元声明:将友元关系授予明确指定的类或函数
- 类模板或函数模板的友元声明:授予对友元所有实例的访问权。
template <class T> friend class Fool;
temolate<class T>friend void temp1(const T&);
- 指收于对类模板或函数模板的特定实例的访问权的友元声明。友元声明前必须声明类或函数。
friend class Foo2<char*>; //error 未声明
friend void temp1<char*> (char* const &); //error 未声明
成员模板
任意类可以拥有本身为类模板或函数模板的成员,这种成员称为成员模板。
例如标准容器的assign成员,接受两个迭代器的assign版本使用模板形参表示其迭代器形参的类型。
定义模板成员
template <class Type> class Queue
{
public:
template<class It>
Queue(It beg,It end):head(0),tail(0) {copy_elems(beg,end);}
template<class Iter> void assign(Iter,Iter);
private:
template<class Iter> void copy_elems(Iter,Iter);
};
成员函数的开关是自己的模板形参表。
template<class T> template<class Iter>
void Queue<T>::assign(Iter beg,Iter end)
{
destory();
copy_elems(beg,end);
}
成员和模板醉熏常规访问控制。
类模板的static成员
template<class T> class Foo
{
public:
static std::size_t count(){return ctr;}
private:
static std::size_t ctd;
};
Foo类的每个实例化都有自己的静态成员。
Foo类型的任意对象共享同一个static成员ctr.
Foo类型的对象共享另一个不同的ctr成员。
泛型句柄类
提供管理使用计数和基础对象的操作。
模板特化
略…
后面高级特性。。。P858