目录
1.模板
1.1模板基本使用
1.2.模板实例化
1.3函数模板的匹配规则
2.类模板
!!!!!!!目前更新到这,后面再更新模板的其他内容,本文章只是第一部分,第二部分等后面我再写!!!!
1模板
1.1模板基本使用
template<typename T> void swap(T& left, T& right) {T temp = left;left = right;right = temp; }int main() {int a = 2, b = 3;double a1 = 2.1, b1 = 3.1;swap(a, b);swap(a1, b1);return 0; } auto不能用在函数参数上,而void*也难以解决自定义类型,所以c++这里又出现了 个模板,用来解决类似的问题。像上面,我们将交换函数用这种方式写,就可以让内置和自定义都能 放进去。 template是关键字,typename可以用class替代,但不能用struct替代 template<typename T1,typename T2,typename T3,typename T4>
注意,T1,T2可以是一样的类型,比如函数参数是T1& x,T2& y 。我们传可以都穿int,最后T1和T2就都会是int类型。
像上面的例子,每次调用不同类型,本质上是调用了不同的函数,编译器自身麻烦点,每次都生成个函数来用,但我们只管传就行。
1.2.模板实例化
template<typename T> T swap(const T& left, const T& right) {return left+right; }int main() {int a = 2, b = 3;double a1 = 2.1, b1 = 3.1;swap(a, a1);swap(b, b1);return 0; } 这个情况,模板参数只有一个T,我们传两个不同类型的参数给函数, 编译器无法确认T到底是什么类型,这样就会报错。 我们可以通过给更多的模板参数,函数的参数也用不同的模板参数还有就是在传参的时候做改变, T add( T& left, T& right) {return left + right; }int main() {int a = 2, b = 3;double a1 = 2.1, b1 = 3.1;int g=add(a, b);g = add(a, (int)a1);cout << g << endl;return 0; } 这时候可以用两种,一个是强转,像上面的一样。一种就是add<int>(a,a1); 也就是显示实例化 但这样还是会报错,为什么呢,因为函数参数我们用了引用,但这样的话,这两种方法本质上都是让传递的参数适应函数模板,并且这两种方法都是产生了,a1都是进行了类型转换,转换后产生的临时变量再传参,但是引用一般不支持临时变量的引用,所以上面的方法必须还得对函数的参数进行改变,如下: T add( const T& left, const T& right)//const加引用,才支持临时变量 {return left + right; }int main() {int a = 2, b = 3;double a1 = 2.1, b1 = 3.1;int g=add(a, b);g = add(a, (int)a1);cout << g << endl;return 0; }
而显示实例化,一般是针对模板函数的参数里面没有模板参数,用上面的例子的话,就是add函数的参数里面没有T类型,而是用在了函数体里面。
1.3函数模板的匹配规则
int Add(double left, double right) {return left + right; } template<class T> T Add(T left, T right) {return left + right; } void Test() {Add(1, 2); // 与非模板函数匹配,编译器不需要特化Add<double>(1, 2); // 调用编译器特化的Add版本 } 非模板函数与同名模板函数可以同时存在。Add(1, 2)这句调用第一个函数,因为对于编译器来说会省下一个实例化模板的操作 但如果 Add<double>(1, 2);的话,就会强制让编译器实例化模板函数。
int Add(int left, int right) {return left + right; } template<class T1, class T2> T1 Add(T1 left, T2 right) {return left + right; } int main() {Add(1,2);这里会直接调用非模板函数Add,因为类型完全匹配Add(1,2.0);//这里,如果没有模板函数,那么也是可以传参的,只是会进行隐式类型转换。//但这里有函数模板,那么编译器就会选择实例化函数模板,产生一个更加匹配的函数来接收参数return 0; } 注意!! 函数模板不支持自动类型转换,普通函数支持
2.类模板
关于类模板,先看下面的代码
class aaa { public:aaa(){b = 10;} private:int b; };int main() {aaa* a = new aaa;return 0; } 这样这个类里面b是固定的int类型,我们如果想让b存别的类型,就得cv一下,再写个别的类型的 但通过类模板,比如 template<typename T> class aaa { public:aaa(T& b){_b = b;} private:T _b; };int main() {aaa<int> a;return 0; } 通过实例化,我们传什么类型,就生成什么类型的类。 实例化之后的才是一个类,上面那个是类模板
如果类模板里声明一个函数,类模板外定义函数,那么在类模板外定义的时候,要先加个
template<typename T>(具体不一定这样,但是重点是加模板参数列表)在函数定义的上面
template<typename T> class aaa { public:aaa(T& b); private:T _b; };template <class T> aaa<T>::aaa(T& b) {//..... }
声明和定义必须在同个文件。