模板
由于模板元编程需要以面向对象为基础,所以如有疑问之处可以先补充一点C++面向对象的知识:
C++面向对象这一篇就够了
泛型初步
由于C++是静态强类型语言,所以变量一经创建,则类型不得更改。如果我们希望创建一种应用广泛地复数类型,那么相应地需要基于int
、float
、double
这些基础类型逐一创建,十分麻烦。泛型编程便是为了简化这一过程而生。
能够容纳不同数据类型作为成员的类被成为模板类,其基本方法为在类声明的上面加上一行模板声明代码
template<typename T>,下一行为class myClass,其调用过程为myClass<T> m。
列举案例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include<iostream> using namespace std; template < typename C> struct Abstract{ C real; //real为C类型 C im; Abstract(C inReal, C inIm){ real = inReal; im = inIm; } void printVal(){ cout<< "Abstract:" <<real<< "+" <<im<< "i" <<endl; }; Abstract& multi(Abstract val){ C temp = real*val.real - im*val.im; im = real*val.real + im*val.im; real = temp; return * this ; }; }; int main(){ Abstract< float > fTemp{1,2}; //C类型为float fTemp.multi(fTemp); fTemp.printVal(); system ( "pause" ); return 0; } |
函数模板
当然,上述multi
并不能实现两个不同类型的Abstract
之间的相乘,所以可以将multi
函数改为
1
2
3
4
5
6
7
|
template < typename T> Abstract<C>& multi(Abstract<T> val){ C temp = real*val.real - im*val.im; im = real*val.real + im*val.im; real = temp; return * this ; } |
这样就能够实现如下功能。
1
2
3
4
5
6
7
8
9
|
int main(){ Abstract< float > fTemp{1,2}; Abstract< int > iTemp{1,2}; fTemp.multi(iTemp); fTemp.printVal(); getReal(fTemp); system ( "pause" ); return 0; } |
友元
模板类具备一部分普通类的性质,比如struct和class的区别,public、protected、private的性质,以及友元等。模板的声明特性也可以应用在函数中,例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include<iostream> using namespace std; template < typename C> class Abstract{ C real; C im; public : Abstract(C inReal, C inIm){ real = inReal; im = inIm; } void printVal(){ cout<< "Abstract:" <<real<< "+" <<im<< "i" <<endl; }; Abstract& multi(Abstract val){ C temp = real*val.real - im*val.im; im = real*val.real + im*val.im; real = temp; return * this ; } template < typename T> friend void getReal(Abstract<T> num); //声明友元 }; template < typename C> void getReal(Abstract<C> num){ cout<<num.real<<endl; } int main(){ Abstract< float > fTemp{1,2}; fTemp.multi(fTemp); fTemp.printVal(); getReal(fTemp); system ( "pause" ); return 0; } |
需要注意的一点是,在模板类中声明友元,其前缀<typename T>
中的类型标识不得与已有的类型标识重复,否则编译无法通过。
由于函数模板可以针对不同的数据类型进行求解操作,是对函数或者方法实例的抽象,所以又被称为算法。
模板参数
如果将模板理解为一种类型声明的函数,那么模板也应该具备一些函数具备的功能。首先其模板参数中可以包含实际类型参数,例如
1
2
|
template < typename T, int max> class Test{} |
其调用时可以写为
1
|
Test< int ,256> pixel; |
模板同样支持默认参数,即可以实现如下形式
1
2
3
|
template < typename T= int , int max=256> class Test{} Test pixle; |
除了数据类型、值之外,模板本身也可以作为模板参数,例如下面的形式是合法的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
template < typename T, template < typename > class C> struct Test{ C<T>* val; Test(C<T>* inVal){ val = inVal; } }; int main(){ Abstract< int > fTemp{1,2}; Test< int ,Abstract> test(&fTemp); test.val->printVal(); system ( "pause" ); return 0; } |
其结果为
1
2
3
4
|
PS E:\Code\cpp> g++ .\generic.cpp PS E:\Code\cpp> .\a.exe Abstract:1+2i 请按任意键继续. . . |
需要注意的一点是,在模板类中定义的模板类,需要进行实例化,否则会出现错误,所以在Test
中,以指针形式创建了模板类。
类型函数
以数据类型为输入或输出的函数即为类型函数,在C语言中,sizeof
便是一种类型函数,其输入为数据类型,输出为数据类型所需要的内存空间。
在C++11中,using
可以实现数据类型赋予的功能,其使用方法与typedef
相似
1
2
3
4
|
template < typename T> struct Test{ using type = T; } |
元编程的基本概念
元编程是泛型编程的一个超集,两者的本质均是针对不同数据类型的算法,后者则更关注传入参数的广泛性。如果将元编程分为四个层次
- 无计算
- 运算符连接的运算
- 编译时具备选择等非递归计算
- 编译时具备递归运算
那么泛型编程可以作为第一类元编程,或者说更加关注的是参数的传入传出过程,而元编程则更关注不同数据类型的选择过程。
例如,我们可以实现一个最多包含三个元素的元组Tuple
,其思路为,三元元素可以看成是一个二元元组与一个参数的组合;二元元组可以看成是一元元组与参数的组合;一元元组则是一个基本数据类型的变量。在这个元组的实现过程中,除了赋值过程实现泛型之外,也需要判断当前所实现的元组元素个数,如果其初始化参量为3个时,需要递归式地创建变量,直到赋值参数为1个。则其实现如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
class Nil{}; //主模板 template < typename T1=Nil, typename T2=Nil, typename T3=Nil> struct Tuple : Tuple<T2,T3>{ T1 x; using Base = Tuple<T2,T3>; //三元元组以二元元组为基础 //返回值为Tuple<T2,T3>指针类型的base()函数 //static_cast将this转化为Base*类型 Base* base(){ return static_cast <Base*>( this );} const Base* base() const { return static_cast < const Base*>( this );} //构造函数继承二元元组,在构造本类中x的同时,构造基类Tuple<T2,T3> Tuple( const T1& t1, const T2& t2, const T3& t3) :Base{t2,t3},x{t1}{} }; template < typename T1> struct Tuple<T1>{ T1 x; }; template < typename T1, typename T2> struct Tuple<T1,T2> : Tuple<T2>{ T1 x; using Base = Tuple<T2>; Base* base(){ return static_cast < const Base*>( this );} const Base* base() const { return static_cast < const Base*>( this );} Tuple( const T1& t1, const T2& t2):Base{t2}, x{t1}{} }; template < typename T1, typename T2, typename T3> void print_elements(ostream& os, const Tuple<T1,T2,T3>& t){ os<<t.x<< "," ; print_elements(os,*t.base()); } template < typename T1, typename T2> void print_elements(ostream& os, const Tuple<T1,T2>& t){ os<<t.x<< "," ; print_elements(os,*t.base()); } template < typename T1> void print_elements(ostream& os, const Tuple<T1>& t){ os<<t.x; } //运算符重载 template < typename T1, typename T2, typename T3> ostream& operator<<(ostream& os, const Tuple<T1,T2,T3>& t){ os<< "{" ; print_elements(os,t); os<< "}" ; return os; } int main(){ Tuple< int , double , char > x{1,2.5, 'a' }; cout<<x<<endl; system ( "pause" ); return 0; } |
其输出结果为
1
2
3
|
PS E:\Code\cpp> g++ .\generic.cpp PS E:\Code\cpp> .\a.exe {1,2.5,a} |
可变参数模板
上述实现过程非常繁琐,而且限制了元组中的元素个数,如果标准库中用上述的书写风格,那么标准库除了这个元组之外也写不了其他的东西了。好在C++模板提供了可变参数的功能,例如,我们可以先将打印模板函数写为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//typename... T 代表可变参数 template < typename T1, typename ... T> void print_elements(ostream& os, const Tuple<T1,T...>& t){ os<<t.x<< "," ; print_elements(os,*t.base()); } template < typename T1> void print_elements(ostream& os, const Tuple<T1>& t){ os<<t.x; } template < typename ... T> ostream& operator<<(ostream& os, const Tuple<T...>& t){ os<< "{" ; print_elements(os,t); os<< "}" ; return os; } |
其输出结果为
1
2
3
4
|
PS E:\Code\cpp> g++ .\generic.cpp PS E:\Code\cpp> .\a.exe {1,2.5,a} 请按任意键继续. . . |
然后将Tuple
也做相同的更改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
template < typename T1, typename ... T> struct Tuple : Tuple<T...>{ T1 x; using Base = Tuple<T...>; //N+1元元组以N元元组为基 Base* base(){ return static_cast <Base*>( this );} const Base* base() const { return static_cast < const Base*>( this );} //注意T&...的书写格式 Tuple( const T1& t1, const T&... t):Base{t...},x{t1}{} }; template < typename T> struct Tuple<T>{ T x; }; /* print模板 */ int main(){ Tuple<string, double , int , char > tt( "hello" ,1.5,1, 'a' ); cout<<tt<<endl; system ( "pause" ); return 0; } |
其输出结果为
1
2
3
|
PS E:\Code\cpp> g++ .\generic.cpp PS E:\Code\cpp> .\a.exe {hello,1.5,1,a} |
以上就是C++元编程语言初步入门详解的详细内容,更多关于C++元编程语言初步的资料请关注服务器之家其它相关文章!
原文链接:https://blog.csdn.net/m0_37816922/article/details/103781346