【摘 要】结合实例介绍了C++11中模板的语法规则,包括函数模板、类模板以及函数和类模板的特化,随后对模板在元编程领域进行了讨论,并总结了元编程的优缺点。
【关键词】C++;元编程;程序设计;
引言
C++模板是支持参数化多态的工具,标准库中如std::vector,std::list等都是模板,可以支持多种类型,用来实现代码的复用。C++中模板分为两类:函数(function)模板和类(class)模板[1]。模板元编程是这两种模板演变而来的一项较为高级的编程技巧,Alexandrescu在2001年发表的《Modern C++ Design》[2]及模板程序库Loki[3]间接地导致了模板元编程库的出现,书中所使用泛型组件等方法令人眼前一亮,由此模板元编程进入人们的视线。本文先介绍了函数模板和类模板,然后讨论了模板元编程。
函数模板
函数模板可以定义一类的函数,这样可以让重复的操作通过抽象聚集到一个函数,减少代码量。函数模板的语法如下:
template <模板参数列表> 函数声明
其中模板参数列表可以是类型,也可以是整型参数,而函数声明跟一般的函数声明一致,不过函数的返回值和参数列表的类型可以是模板参数列表中声明的类型。下面是一个相同类型相加的函数Add:
template <typename T>
auto Add(T a,T b) -> decltype(a + b){
return a + b;
}
对于这样的实现,我们可以这样调用:
std::cout << Add(10,10)<< std::endl;
对于函数模板,编译器会自动扩展已经实列化的模板。上述例子,编译器会自动生int Add(int a,int b)函数。
上述例子表明:函数模板是一系列函数的抽象,形参可以是允许的任意类型,使用相同函数模板可以实现不同数据类型的相同运算操作。
类模板
类模板的语法如下:
template <模板参数列表> 类的声明
模板参数列表与函数模板相同,类的成员以及类的成员函数可以使用模板参数列表声明的类型和常量,类的成员函数也可以是函数模板。我们可以借助类模板来实现单例设计模式,代码如下:
template <class T>
class Singleton {
protected:
Singleton(){}
~Singleton(){}
public:
static T& Instance(){
static T gInstance;
return gInstance;
}
};
使用时只需要继承这个类,就不需要重复实现Instance方法了,有效地复用了代码。
模板元编程
基于函数模板和类模板,人们提出了模板元编程的编程方法。模版元编程完全不同于一般C++的运行期程序,它非常独特。这是因为模版元程序的执行时期是在编译期,且模版元程序操纵的数据不能是运行时变量,只能是编译期不可修改的常量,外加它可以用到的语法元素相当有限,无法使用运行期的某些语法,比如if-else逻辑分支,for循环语句等都不能用来进行模板元编程。因此,模版元编程需要多项技巧来配合,导致编写模版元编程比较复杂也比较困难。下面我们使用模板元编程来实现整数的求和:
template <int...> struct Sum;
template <int A> struct Sum<A> {
static constexpr int value = A;
};
template <int A,int B> struct Sum<A,B> {
static constexpr int value = A + B;
};
template <int A,int B,int...Args> struct Sum<A,B,Args...> {
static constexpr int value = Sum<A,B>::value + Sum<Args...>::value;
};
上述例子通過VS2019的编译。使用方法如下:
std::cout << Sum<1,2>()<< std::endl;// print 3
std::cout << Sum<1,2,3,4,5>()<< std::endl;// print 15
但是这样使用:
int a = 1,b = 2;
std::cout << Sum<a,b>()<< std::endl;
会出现编译错误。因为模板是编译时期确定的,不能像函数一样可以传入变量,它只能是常量。可以发现:模板元编程只操作在编译期可以确定的量,编程思维跟普通C++程序开发有很大的区别,而且语法较为繁琐;由于元编程在大数情况下,用到了递归,而元编程又是在编译期确定的,这样会拉长程序的编译时间;在C++11中,编译器对模板的错误提示并不友好,这样会导致元编程在调试方面有极大的障碍。但是模板元编程得到的计算可以说是一个常量,理论上无论其复杂度多大,在运行时去访问,其复杂度为O(1),同时也能在一定程度上减少程序发布大小。
总结
因为模板是泛化了一系列类型的相同操作,这样比不用模板,更能有效的复用代码,减少代码量。而在C++11中,模板元编程在编译期就能确定计算结果,但会增加编译时间,以及调试困难等缺点,在应用模板编程时,需要全面权衡。
参考文献:
[1] 赵娟.模板在C++中的应用[J].电脑知识与技术,2019(20).
[2] Andrei Alexandrescu.Modern C++ Design[M].US:Addison-Wesley Professional,2002
[3] Andrei Alexandrescu.”Loki Library” [OL]http://loki-lib.sourceforge.net/
作者简介:
方言(1995年-),湖南人,宁夏大学硕士研究生在读,主要研究物联网技术方向。
(作者单位:宁夏大学信息工程学院)