C Templates学习笔记⑦:深入模板基础

对前面一些基础知识的深入和补充。


一些模板例子:

template <typename T>
class List {
public:
	template <typename T2>
	List(List<T2> const&);
};

template <typename T>
template <typename T2>
List<T>::List(List<T2> const& b)
{
	//todo
}

template <typename T>
int length(List<T> const&);

class Collection {
	template <typename T>
	class Node {
		//todo
	};

	template <typename T>
	class Handle;

	template <typename T>
	T* allco{

	}
};

template <typename T>
class Collection::Handle {
	//todo
};

一:虚成员函数

成员函数模板不能声明为虚函数。

因为成员函数模板实例化数量不确定 导致无法确定虚表大小,这与虚函数表设计相冲突。

相反的,类模板的非模板成员函数可以是虚函数。

二:模板的链接

模板在所属作用域下名字必须是唯一的。(1.类模板不能和另外一个实体共享 2.函数模板重载除外)

模板名字具有链接但是不能有c链接。

扫描二维码关注公众号,回复: 9536941 查看本文章

三:模板参数

1.类型参数

2.非类型参数

包括:整型或者枚举类型,指针类型,引用类型

函数和数组类型也可以被指定为非模板参数,但是需要先隐式转换为指针类型

template<int buff[5]> class Lexer;//
template<int* buff> class Lexer;//等价

3.模板的模板参数

声明和类模板的声明类似,但不能使用关键字struct和union

4.缺省模板实参

缺省实参不能依赖于自身的参数,但可以依赖于前面的参数

template <typename T, typename Allocator = allocator<T> >
class List;//allocator<T>不能依赖于本身的参数Allocator 但是可以依赖于前面的参数T
template <typename T1, typename T2, typename T3, typename T4 = char, typename T5 = char>
class QQ;

template <typename T1, typename T2, typename T3 = char, typename T4, typename T5>//正确 因为上面一个的4,5已经有缺省值了
class QQ;

template <typename T1 = char, typename T2, typename T3, typename T4, typename T5>//错误 2还没有缺省
class QQ;


template <typename T = void>
class Value;

template <typename T = void>
class Value;//错误:重复出现的缺省值

5.模板实参

注:

包括:显式模板实参,注入式类名称,缺省模板实参,实参演绎。

注:对于函数模板实参演绎,可以通过调换模板参数的顺序将无法演绎的参数放在前面,让后面的参数依旧可以演绎。

注:替换失败并非错误是函数模板可以重载的重要因素。允许试图创建无效的类型,但是不允许试图计算无效的表达式

注:在函数定义内部声明的类型不能作为模板的类型实参,未命名的class类型或者未命名的枚举类型不能作为模板的类型实参,通过tepedef声明给出的未命名类和枚举事可以作为模板类型参数的。

例如:

template <typename T> class List {
	//todo
};

typedef struct {
	double x, y, z;
}Point;

typedef enum {red, green, blue} *ColorPtr;

int main()
{
	struct Association
	{
		int *p;
		int *q;
	};
	List<Association*> error1;//错误:模板实参中使用了局部类型
	List<ColorPtr> error2;//错误: 模板实参中使用了未命名的类型因为typedef定义的是 *ColorPtr, 并非ColorPtr

	List<Point> ok;//正确 通过使用typedef定义的未命名类型
}

当创建程序时,编译器或者链接器要能够确定实参的值,如果实参的值要等到程序运行时才能确定(局部变量的地址等) 就与之相悖了。

特别的:

空指针常量,浮点类型,字符串这些常值不能作为有效的非类型实参。

对于字符串类型 可以用一个额外变量存储 调用

一些错误例子:

template<typename T, T nontype_para>
class C;

class Base {
public:
	int i;
} base;

class Derived : public Base{

}derived_obj;

C<Base*, &derived_obj>* err1; //这里不会产生派生类型向基类的转换

C<int&, base.i>* err2;       //域运算符后面的变量不会被看成变量

int a[10];
C<int*, &a[0]>* eer3; //单一数组元素地址不可取

6.友元

1.友元类

友元类的声明可以命名一个特定的类模板实例为友元,但这时声明的地方类模板必须是可见的,与普通类不同

例如:

template <typename T>
class Tree {
	friend class Factory;//Factory可以不可见
	friend class Node<T>;//node 必须可见
};

2.友元函数

在普通类里面声明友元函数。

注:命名一个实例的友元声明是不能作为定义的。

void multiply(void*);

template <typename T>
void multiply(T);

class Comrades {
	friend void multiply(int){}//定义一个新的函数  非受限函数不能引用模板函数


	friend void ::multiply(void*);  //引用上面普通函数  没有<>普通函数优先度高

	friend void ::multiply(int); //引用模板实例 因为普通函数不匹配

	friend void ::multiply<double*>(double*); // 引用模板 但模板在此必须可见

	friend void ::error() {}//错误 受限的友元不能是定义
};

在模板类里面胜面友元,与上面类似,add:可以使用模板参数来标识友元函数

template <typename T>
class Node {
	Node<T>* allocate();
};

template <typename T>
class List {
	friend Node<T>* Node<T>::allocate();
};

注:在类模板内部定义一个友元函数的时候 如果该友元函数 在不同模板实例化后类型一样 那么会产生重定义的问题。

如:

template <typename T>
class Creator {
	friend void appear() {

	}
};

Creator<void> miracle;
Creator<double> oops;//::appear()被第二次生成

注:类内定义友元函数是普通函数 而不是 模板的实例

由于函数的实体在类定义内部,所以是内联函数,在两个不同的翻译单元中可以生成相同的函数。

class Manager {
	template <typename T>
	friend class Task;

	template <typename T>
	friend void Schedule<T>::dispatch(Task<T>*);

	template <typename T>
	friend int ticket()
	{
		return ++Manager::counter;
	}

	static int counter;
};

注 友元模板:让模板的所有实例都成为友元

发布了35 篇原创文章 · 获赞 5 · 访问量 419

猜你喜欢

转载自blog.csdn.net/qq_33776188/article/details/102955550
今日推荐