C++/模板
C++的模板(Templates)是为了解决类型参数化问题,允许在编写代码时不固定类型,而是在编译时根据需要传入具体类型,支持类型的参数化。
C++模板是在编译时进行实例化的。C++编译器根据模板的定义生成不同类型的代码,并在编译过程中根据实际传入的类型生成相应的类或函数。
- 这意味着模板是静态的,编译器在编译期间确定最终的类型。
- 通过模板,C++可以进行“零开销抽象”,即编译器优化掉模板相关的代码,最终生成非常高效的机器代码。
C++模板非常强大,可以在编译时进行复杂的计算,这种特性被称为模板元编程。通过SFINAE(Substitution Failure Is Not An Error)等机制,C++模板可以根据类型的特性做出条件选择。可以通过递归模板、SFINAE、类型萃取等方式实现强大的功能。
C++11及之后的版本引入了变长模板参数(Variadic Templates),允许模板参数的数量是可变的,这使得C++可以编写更加灵活的泛型代码。例如,使用std::tuple和std::make_tuple等功能。
模板形参
[编辑]模板定义以关键字template开始,后接模板形参表(template parameter list),模板形参表是用尖括号括住的一个或者多个模板形参的列表,形参之间以逗号分隔。模板形参可以是:
- 表示类型的类型形参(type parameter)。类型形参跟在关键字class或typename之后声明。不允许cv限定。在 C++11 之前,类型的模板实参不允许是局部类型(local type)、无链接性的类型(type with no linkage)、无名类型(unnamed type)或包括了这三种情形的复合类型。因为这些类型不具备全局链接性或命名。然而,C++11 以后,这些限制被放宽,允许使用局部类型、无名类型等作为模板参数,使得模板编程更加灵活和强大。
- 表示常量表达式的非类型形参(non-type parameter)。非类型形参跟在类型说明符之后声明。允许用const或volatile限定。不允许声明为浮点型、class类型、void型。允许声明为下述形式:
- 整型或枚举型
- 到对象的指针或函数指针,必须是常量指针
- 到对象的引用或函数引用,必须是常量引用
- 成员指针
- nullptr_t
- 模板的非类型参数被声明为数组或函数的,将被decay为指针或函数指针。例如:
模板特化
[编辑]模板特化允许对特定类型做优化,或者去修正模板对某一特定类型实例化之后的行为。
模板特化分为:
- 全特化:
- 偏特化:
别名模板
[编辑]别名模板(aliase template)是C++11引入的技术。在C++03标准中,可以用typedef给全特化模板定义新的类型名。但是不允许用typedef施加于偏特化模板上。C++11增加了给偏特化模板增加别名的功能,例如:
template <typename First, typename Second, int Third>
class SomeType;
template <typename Second>
using TypedefName = SomeType<OtherType, Second, 5>;
//using在C++11中也可用于其他的类型别名的声明:
typedef void (*FunctionType)(double); // Old style
using FunctionType1 = void (*)(double); // New introduced syntax
默认模板参数
[编辑]模板形参可以给出默认值(default arguments for template parameters)。如果一个模板参数给出了默认值,那么模板形参列表中在其后声明的模板参数都应该给出默认值。例如:
template<class T = char, class U, class V = int> class X { }; //编译出错,或者给出U的默认值,或者不给出T的默认值
可变参数模板
[编辑]省略号(...)在可变参数模板中有两种用途:
- 省略号出现在形参名字左侧,声明了一个参数包(parameter pack)[1]。使用这个参数包,可以绑定0个或多个模板实参给这个模板参数包。参数包也可以用于非类型的模板参数。
- 省略号出现在包含参数包的表达式的右侧,称作参数包展开(parameter pack expansion),是把这个参数包解开为一组实参,使得在省略号前的整个表达式使用每个被解开的实参完成求值,所有表达式求值结果被逗号分开。这种表达式必须是可接受任意个数的以逗号分开的子表达式。注意这里的逗号不是作为逗号运算符。
如果在一个参数包展开中同时出现了两个参数包的名字,则二者在一起同时展开,且应该具有相同长度。这可能出现在类模板带有一个参数包,它的嵌套的类模板或成员函数模板带有另一个参数包。