C++模板学习笔记

需要区分typename和class的情况

当我们写如下代码的时候:

template<class T>
class kkk {
	T::t i;//编译器不知道这个t是一个类型还是对象成员,所以不敢直接定义i,报错
    typename T::t i;// typename就是明确告诉编译器这里是类型,等模板实例化再去找
	T i;//这样是没有问题的,因为已经在模板中声明了T是一个类类型
};

直接使用auto来定义变量基本不会有这种问题

非类型模板参数

在template中除了类对象和类型,还可以搞一个非类型模板参数,一般用在array里面,用于指定类中的静态数组的大小

注意:这个参数只接受整形,浮点数、类对象以及字符串不允许作为非类型模板参数的

template<class T1,typename T2,size_t n>
class array {
private:
	int arr[n];
};

模板的特化

一般模板就是用来接收不确定的类型的,但是假如这个函数需要特别处理的(比如比大小时传过去指针,然而预期比较的是指针解引用后元素的大小而非地址的高低),就需要用到模板的特化

函数模板的特化:

见下面代码

template<typename T>
bool Greater(T p1,T p2) {
	return p1 < p2;
}

template<>
bool Greater<int*>(int* p1, int* p2) {
//当调用Greater函数时,参数为int*时,会调用这个函数而非第一个
    cout<<"*p1>*p2"<<endl;	
}

int main() {
	int a = 2, b = 3;
	Greater(&a, &b);//输出:*p1>*p2
	return 0;
}

然而实现相同的功能,完全可以写成重载的形式,这种方式更加简介,所以一般不推荐写函数的模板特化

bool Greater(int* p1, int* p2) {
	return *p1 > *p2;
}

类模板的特化

模板的全特化:

当需要特化的全部参数类型已知的时候,写成如下形式

template<typename T1, typename T2>
class Less {
	bool operator()(T1) {
		return *T1 > *T2;
	}
};

template<>
//这里的尖括号说明不确定的类型的,而下面的括号就是用来指出哪些类型需要特殊匹配的
//由于全特化需要特殊匹配的类型全部已知(int和char),所以直接下面写明即可
class Less<int,char> {
	bool operator()() {
		cout << "int and char" << endl;
		return 0;
	}
};

类模板的偏特化:分为部分参数特化和参数模式特化

部分参数特化

固定部分模板参数,让剩余参数保持通用

template<class T1,class T2,class T3>
class test {
	test() {
		cout<<"T1,T2,T3"<<endl
	}
};

template<class T1>//这里T1是不确定的
class test<T1,int,char> {
	test() {
		cout << "T1,int,char" << endl
	}
};

int main(){
    test(int ,int ,char);//输出T1,int,char
}

注意:偏特化上下尖括号内的顺序不一定一致,上面说明有哪几个不确定的类型,下面的尖括号从上面的尖括号里取来用而已

参数模式特化

固定具体类型,而是对参数形式添加约束(如指针、引用、数组等)

template<typename T>
class dssa {
public:
    dssa() {
        cout << "this time is normal template" << endl;
    }
};

template<typename T>
class dssa<T*> {//假如传递的是T类型的指针的时候会触发
public:
    dssa() {
        cout << "this time is T*"<<endl;
    }
};

int main() {
    dssa<int*> miao;
    //输出:this time is T*
    return 0;
}

为什么传一个int不会匹配到第一个模板呢?

两个都匹配时C++规定偏特化更优先

混合使用上述两种方式:
template<typename T1, typename T2>
class kkk {
public:
	kkk() {
		cout << "this time is normal template" << endl;
	}
};

template<typename T1>
class kkk<T1*,int> {
public:
	kkk() {
		cout << "this time is normal template" << endl;
	}
};

模板参数的分离编译

如果是普通类,其成员函数的声明和定义是可以分开的;而模板类在编译过程中,由于声明和定义是分不同文件编译的,当编译看到模板类函数的定义时,因不知道T是什么类型,不会对定义里面的代码实例化,调用该函数时,无法找到对应代码,报错。见下面示例代码:

//test.h
template<class T>
class lll {
	bool less(T p1,T p2);
};

//test.cpp
template<class T>
bool lll<T>::less(T p1,T p2) {
	return p1 < p2;
}//不知道T是谁,那就不实例化这段代码

//main.cpp
#include "test.h"//include的时候发生的是代码的替换
//下面这段代码被引入
template<class T>
class lll {
	bool less(T p1,T p2);//由于定义的代码没有被实例化,这里引入的其实是一具空壳
};
//

int main(){
    lll<int>::less(1,2);//调用的时候发现没有代码的实现,报错
    return 0;
}

解决方式

显示实例化

直接在类里面实例化一个特定的类型,但这种方式,每一个不同的类型都要实例化一次,非常冗杂

在上面代码的test文件中
//test.cpp
template<class T>
bool lll<T>::less(T p1,T p2) {
	return p1 < p2;
}
template class lll<int>;//生成lll的int版本方法


#include "test.h"
int main(){
lll<int>::less(1,2);//想要调用Int版本,链接到了test.cpp中生成的方法,不报错
return 0;}

在同一个文件中进行声明和定义的分离

推荐使用这种方法,写代码的时候注意一下就好了

//都位于一个文件中,就不会出事
template<class T>
class lll {
	bool less(T p1,T p2);
};

template<class T>
bool lll<T>::less(T p1,T p2) {
	return p1 < p2;
}

模板总结

使用模板的优点

1. 模板复用了代码,节省资源,更快的迭代开发

2. 增强了代码的灵活性

使用模板的缺点

1. 模板会导致代码膨胀问题,也会导致编译时间变长

2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值