个人主页: :✨✨✨初阶牛✨✨✨
强烈推荐优质专栏: C++的世界(持续更新中)
推荐专栏1: C语言初阶
推荐专栏2: C语言进阶
个人信条: 知行合一
本篇简介:>:讲解C++中有关模板的其他知识,非类型模板参数,模板特化,模板的分离编译.
金句分享:
✨车如流水马如龙,花月正春风.✨
我们先简单复习一下前面我们见到的普通的模板
实现多种类型的 乘法计算器
namespace cjn{
//普通的单模板参数
template <class T>
void mult(T& e1,T& e2)
{
std::cout<<e1*e2<<std::endl;
}
};
测试:
void test1()
{
int a=2,b=3;
cjn::mult(a,b);
double d1=1.2,d2=5.4;
cjn::mult(d1,d2);
}
运行结果:
6
6.48
上面,模板参数只是一个简单的类型参数,那什么是非类型模板参数呢?
非类型模板参数是指在模板中可以使用的不是类型的参数。该参数在编译期间就已经确定其值,即被称为编译期常量。
非类型模板参数可以用于指定模板实例的一些固定的值,例如容器大小、数组大小等。这样做的好处是提高了代码的灵活性和效率,使我们能够更有效地编写C++
程序。
示例:通过参数控制My_stack
类中数组的大小.
namespace cjn{
//非类型模板参数
template <class T,size_t N=10>
class My_stack
{
public:
int size()
{
return sizeof(arr)/sizeof(arr[0]);
}
//... 成员函数
private:
int arr[N];
};
};
测试:
void test2() //测试 非类型模板参数
{
cjn::My_stack<int,100> my_stack1;
std::cout<<my_stack1.size()<<std::endl;
cjn::My_stack<int,66> my_stack2;
std::cout<<my_stack2.size()<<std::endl;
}
运行结果:
100
66
显然,非类型模板参数可以很灵活的帮助我们实现程序,提高代码的效率,一定程度上避免代码的冗余.
模板是与类型无关的,但是,对于一些类型,往往需要特殊处理,
例如:
(1) 比较元素的大小时,对于字符串的比较方式.
默认:按字典序比较.
目标:有的时候我们需要按长度比较.
(2)对于比较两个值的大小,如果是指针类型,直接比较指针显然是不合适的,需要对指针类型比较往往是先解引用在比较.
等等特殊情况.
对于上面所出现的各种情况,我们往往需要特殊处理.
函数模板的特化步骤:
template
后面接一对空的尖括号<>
.<>
中指定需要特化的类型//模板的特化
template<class T>
bool cmp(T& e1,T& e2)
{
return e1<e2;
}
template<>
bool cmp<string>(string& e1,string& e2)
{
return e1.size() < e2.size();//想让string按长度比较
}
测试:
void test3()
{
int a=12,b=3; //12<3 ?
cout << "a b :" << cjn::cmp(a, b) << endl;
double d1=1.2,d2=5.4; //1.2<5.4 ?
cout << "s1 d2:" << cjn::cmp(d1, d2) << endl;
string s1 = "fffg"; //fffg
string s2 = "abcdefghijklmn";
cout<<"s1 s2 :" << cjn::cmp(s1, s2) <<endl;
}
运行结果:
a b :0
s1 d2:1
s1 s2 :1
全特化:
将模板参数列表中所有的参数都确定化。
示例:对
参数的类,进行特殊处理.
//类模板
template<class T1,class T2>
class A
{
public:
A()
{
cout << "我是正常的类模板" << endl;
cout << typeid(T1).name() << " " << typeid(T2).name() << endl << endl;
}
private:
T1 _a=0;
T2 _b=0;
};
//全特化
template<>
class A< string, int>
{
public:
A()
{
cout << "我是全特化" << endl;
cout << "string, int" << endl << endl;
}
private:
int _a=0;
int _b=0;
};
(1) 参数列表中的部分参数指定.
(2)除了指特化部分参数,针对模板参数更进一步的条件限制所设计出来的一个特化版本也属于偏特化.
//偏特化 1
template<class T>
class A< string, T>
{
public:
A()
{
cout << "我是偏特化 1:" << endl;
cout << "string, T" << endl << endl;
}
private:
int _a=0;
int _b=0;
};
//偏特化 2
template<class T1,class T2>
class A< T1*, T2*>
{
public:
A()
{
cout << "我是偏特化 2:" << endl;
cout << "T1*,T2*" << endl << endl;
}
private:
int _a=0;
int _b=0;
};
运行结果:
我是正常的类模板
int double
我是全特化
string, int
我是偏特化 1:
string, T
我是偏特化 2:
T1*,T2*
在之前,我们经常将函数的声明和定义分开,进行分离编译,那模板也可以分离编译吗?
运行结果:
错误分析:
C/C++
程序要运行,一般要经历一下步骤:
预处理 -> 编译 -> 汇编 -> 链接 的过程.
编译:对程序按照语言特性进行词法、语法、语义分析,错误检查无误后生成汇编代码
注意头文件不参与编译编译器对工程中的多个源文件是分离开单独编译的。
链接:将多个obj
文件合并成一个,并处理没有解决的地址问题
那么对于模板的分离编译操作
模板参数没有得到 类型的实例化,就无法得到地址,这也就导致了在链接过程中的链接错误.
//template.cpp
#include "test.h"
template <class T>
T add(T e1, T e2)
{
return e1 + e2;
}
//显示实例化
template
int add<int>(int e1, int e2);
template
double add<double>(double e1, double e2);
显示实例化虽然可以让编译器通过编译,但是没增加一个类型,就需要修改源文件,增加一个实例化,这就失去了模板的意义,所以.并不推荐.
还是建议将模板定义和声明都写在一个"xx.h"
文件中更加方便.
C++
的模板是一种通用的代码机制,用于在编译时生成具体代码。它允许定义类型和函数,具体实现可以在编译时根据实际数据类型进行实例化。C++
的模板主要分为函数模板和类模板两种。
函数模板允许定义通用函数,其类型可以在编译时由实参推断得出,或者显式指定。函数模板可以与普通函数重载,以满足不同的需求。
类模板允许定义通用类,其类型可以在编译时由实参推断得出,或者显式指定。类模板可以在编译时生成具体类的实例,以满足不同的需求。
【优点】
1). 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2). 增强了代码的灵活性
3). 可以提高代码的重用性和编写效率,并且能够支持更多的数据类型和操作。
【缺点】
1). 模板会导致代码膨胀问题,也会导致编译时间变长
2). 出现模板编译错误时,错误信息非常凌乱,不易定位错误
3). 模板也可能会增加代码的复杂性和可读性。