两种模板(C++03) -> 四种模板(C++14)
传统C++只有两种模板,分别是类模板和函数模板。而最新的C++标准中总共有四种模板,这是因为C++11引入了别名模板,而C++14则引入了变量模板。
template<class T> 和 template<typename T> 都可以用来定义函数模板和类模板,在使用上,他们俩没有本质的区别。
具体为;class用于定义类,在模板引入c++后,最初定义模板的方法为:template,这里class关键字表明T是一个类型。后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字。它的作用同class一样表明后面的符号为一个类型。这样在定义模板的时候就能够使用以下的方式了: template.在模板定义语法中关键字class与typename的作用全然一样。
函数模板针对仅参数类型不同的函数;类模板针对仅数据成员和成员函数类型不同的类。
函数模板:
声明及定义形式为:
template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表) { 函数体 }
在头文件
template < class T>
bool compare(T t1, T t2)
{
if (t1 > t2)
{
return true;
}
else
{
return false;
}
}
调用的地方: compare<int>(1,2);
延伸到默认模板参数
函数模板的默认模板参数没有此约束。但如果是默认参数值还是要由右到左
template <class T1 = float, int i = 0, class T2 = string>
void funt1(T1 t1, T2 t2 = "bbbb" ) {
cout << t1 << i<< t2 << endl;
}
funt1("a");
template是定义模板函数的关键字;template后面的尖括号不能省略;typename(或class)是声明数据类型參数标识符的关键字。用以说明它后面的标识符是数据类型标识符。这样,在以后定义的这个函数中,凡希望依据实參数据类型来确定数据类型的变量,都能够用数据类型參数标识符来说明,从而使这个变量能够适应不同的数据类型。
如:template <class T> void swap(T& a,T& b){}
使用函数模板有2种方式
1.自动类型推到调用swap(a,b);
2.显性类型推到调用swap<int>(a,b);
实际应用
int a =1;
int b= 2;
swap<int>(a,b);
类模板:
其格式为:
template<class 形参名,class 形参名,…> class 类名 { ... };
foo 为类名,在类定义体中,如採用通用数据类型的成员,函数參数的前面需加上T。当中通用类型T能够作为普通成员变量的类型,还能够作为const和static成员变量以及成员函数的參数和返回类型之用。
如:
template <class T>
class A {
public:
T a;
T b;
T hy(T c, T &d);
};
在类A中声明了两个类型为T的成员变量a和b,还声明了一个返回类型为T带两个参数类型为T的函数hy。
用类模板定义对象的写法如下:
类模板名<真实类型参数表> 对象名(构造函数实际参数表);
如果类模板有无参构造函数,那么也可以使用如下写法:
类模板名 <真实类型参数表> 对象名;
template<class T1,class T2>
class A
{
public:
T1 g(T1 a, T2 b);
A();
};
template<class T1, class T2>
A<T1, T2>::A()
{
}
template<class T1, class T2>
T1 A<T1, T2>::g(T1 a, T2 b)
{
return a + b;
}
int main()
{
A<float, int> ta;
ta.g(1.1, 2);
return 0;
}
进一步研究:类模板与继承
1.当子类不是类模板,而继承的父类是一个类模板时,子类在声明的时候要指定出父类中的类型;
//类模板与继承
template<typename T>
class Basic {
public:
T age;
};
子类不是类模板时,可以直接调用基类类模板的函数,因为基类的模板类型已经指定了
//此处需要指定父类中的模板数据类型
class t1 :public Basic<string> {
};
void func1() {
t1 ttt;
ttt.age = "abc";
cout << ttt.age << endl;
}
2.子类是类模板,继承的父类也是一个类模板时。
template <class T>
class tem {
public:
T aa;
int bb;
void func(T t);
void funct() {
int dd = 1;
}
};
template <class T1,class T2>
class tem3 :public tem<T1>
{
public:
using tem<T1>::funct;
int cc;
T2 dd;
void functem3() {
funct();
}
};
tem3<int, int> tt3;
tt3.functem3();
3. 类模板的默认模板参数需要从右到左依次;
template <class T1 = int, int i = 0, class T2 = string>
class ctem
{
public:
T1 t1;
void fun1(T2 t2);
};
template <class T1 /*= int*/, int i /*= 0*/, class T2 /*= string*/>
void ctem<T1, i, T2>::fun1(T2 t2)
{
}
ctem<> tem;
tem.fun1("q");
4.当子类类模板调用基类模板的函数时:直接调用会报错,见下图
解决办法可以有3种办法:
1.在调用动作之前加上“this->”
2.使用using声明式(using tem<T1>::funct;)
3.明白指出被调用的函数位于base class内(baseclass::func())最好不用这办法,因为实现不了虚函数的多态。
别名模板(alias template)
别名模板:带模板参数的类型别名
类型别名(type alias)是C++11新引入的语法形式:
using newtype = oldtype;
在语法功能上,它相当于传统C/C++语言中的typedef语句:
typedef oldtype newtype;
可以看出,类型别名中原有类型和别名的定义顺序与typedef语句正好相反。除此之外,类型别名与typedef语句还有一点不同,类型别名可以加上模板参数形成别名模板:
template<typename ...> using newtype = oldtype<...>;
注:C++11引入类型别名意图取代typedef语句的原因在于:无法直接在typedef语句的基础上直接构建别名模板。这是因为typedef语句自身存在某些局限性,直接给typedef加上模板参数会带来语法解析上的问题。
template<typename T, typename U>
struct A;
template<typename T>
struct B
{
typedef A<T, int> type;
};
template<typename T> using C = A<T, int>;
template<typename T> using D = typename B<T>::type;
代码说明:
假设我们有一个带两个模板参数T和U的类模板A。现在我们需要声明一个只带一个模板参数T的类模板,使其等价于模板参数U为int类型的A模板。也就是说,我们需要一个模板参数T任意,模板参数U为int类型的A模板的别名,或者说A<T, int>的别名。
在C++11之前,答案为类模板B。要定义类型别名,必然要使用typedef。但由于typedef不能带模板参数,所以typedef必须被嵌入到一个带模板参数的类模板里面。在模板参数为T的类模板B里面,类型type被定义成A<T, int>的别名。也就是说typename B<T>::type被定义成了A<T, int>的别名。
在C++11之后,答案为别名模板C。类型别名直接就可以带模板参数。C<T>直接被定义成了A<T, int>的别名。
如果出于某种原因,在定义别名的时候无法使用类模板A而只能使用类模板B,别名模板也能发挥作用。这里D<T>直接被定义成了typename B<T>::type的别名。由于后者是A<T, int>的别名,所以D<T>其实也是A<T, int>的别名。
这段代码展示了别名模板的主要用途:1.为部分模板参数固定的类模板提供别名。2.为类模板中嵌入的类型定义提供别名。
变量模板(variable template)
变量模板:变量的家族
变量模板是C++14新引入的模板形式,在语法功能上它相当于一个没有参数但是有返回值的函数模板,相对于后者,变量模板在语法上更清晰,更能反映编程者的意图。
template<typename ...> type variable;
#include <iostream>
#include <iomanip>
using namespace std;
// 函数模板
template<class T>
constexpr T pi_fn()
{
return T(3.1415926535897932385);
}
// 变量模板
template<class T>
constexpr T pi = T(3.1415926535897932385);
int main()
{
cout << pi_fn<int>() << pi<int> << '\n'; // 33
cout << setprecision(10) << pi<float> << '\n'; // 3.141592741
cout << setprecision(10) << pi<double> << '\n'; // 3.141592654
}
更多推荐
C++中 模板Template的使用
发布评论