模板元编程(Template Meta programming,TMP)
就是面向模板编程,把计算过程从运行时提前到编译期,提升性能;
区别于泛型编程(利用模板实现"安全的宏")
应用场景 : 编译期 数值计算 、类型计算、代码循环展开
思考题 :
用装饰器模式给存在已久的多个类(xx_config类)添加新功能: 读取 json/yaml/xml/ini 配置文件对应部分数据到本类私有成员(不同数量和类型)。就是用一句话添加新功能,注释这句话后,新功能消失
特点:
模板执行完全在编译期
操作的数据对象只能是常量 (如enum,静态常量,基本数据类型,自定义数据类型),
不能是运行期的变量,不能用 if else for while;
其计算能力受到具体编译器实现的限制(如递归嵌套深度,C++98 要求至少 17,C++11 要求至少 1024)
技巧:
- if-else逻辑可用 type_traits(类型特征)实现在编译期做判断,计算、查询、转换、选择
- for 逻辑可用递归、重载、继承、偏特化 实现
一、核心概念:元函数(不是一个函数,功能类似函数)
(根据模板传参分为:"类型type"元函数,"值value"元函数)
函数三要素:返回值,调用方式,参数
约定 :
对于'类型'元函数,将保存返回结果的类型别名用 ::type 命名
对于'值'元函数,将保存返回结果的静态成员用 ::value 命名
1、"值"元函数: 模板传参是'值'
cpp
#include <iostream>
using namespace std;
// 功能:求两个数的最大公约数
// 主模板:递归调用(关键点1),class 可以换成 struct
template < unsigned M, unsigned N > // 1.元函数的参数: 模板参数
class gcd { // 元函数(不是一个函数,元函数发生在编译期,而通常函数调用发生在运行时)
public:
static constexpr int value = gcd< N, M%N >::value; // 2.元函数的返回值:类模板的静态成员常量
};
// 特化模板:处理边界(关键点1)
template< unsigned M >
class gcd< M, 0 > {
public:
static_assert(M != 0);
static constexpr int value = M;
};
int main() {
// 3.元函数的调用 通过::访问类模板的成员
int value=gcd<12,6>::value; // 值作为模板参数
cout << value << endl; // 输出 6
}
2、"类型"元函数: 模板传参是'类型',普通函数做不到
cpp
#include <iostream>
using namespace std;
// 功能:返回给定类型数组的维度
// 主模板, struct 可以换成 class + public:
template< typename T >
struct dim { static constexpr size_t value = 0u;}; // 当类型不是数组类型时,返回0
// 特化1: 递归也可以放在特化中,而不一定在主模板中
template< typename U, size_t N >
struct dim< U[N] > { static constexpr size_t value = 1u + dim< U >::value; };
// 特化2
template< typename U >
struct dim< U[] > { static constexpr size_t value = 1u + dim< U >::value; };
int main() {
using array_t = int[10][20][30]; // 类型别名
int arrry_dim = dim<array_t>::value; // 类型作为模板参数
cout << "维度 = " << arrry_dim << endl; // 维度 = 3
}
对上面的优化版(这种更常见)
cpp
// 标准库中有一个值叫 value 的静态成员常量元函数
template <typename T, T v> struct integral_constant { static constexpr T value = v; /*....*/ };
// 下面几个继承 integral_constant, 就有静态成员 value 了
template <typename T>
struct dim : integral_constant<std::size_t, 0u> {};
template <typename U, std::size_t N>
struct dim <U[N]> : integral_constant<std::size_t, 1u + dim<U>::value > {};
template <typename U>
struct dim <U[]> : integral_constant<std::size_t, 1u + dim<U>::value > {};
3、"类型"元函数: 模板返回值是'类型',普通函数做不到
cpp
// 功能:返回移除 const 限定符的相同类型
template<typename T>
struct remove_const {
using type = T; // 没有const 正常返回
};
template<typename T>
struct remove_const<T const> {
using type = T; // 移除了const 的相同类型
};
对上面的优化版(这种更常见)
cpp
template <typename T> struct type_is { using type = T; }; // 返回传入的类型, 统一命名 type
// 下面几个继承 type_is, 就有 type 了
template <typename T> struct remove_const: type_is< T > {}; // 没有 const 正常返回
template <typename T> struct remove_const<T const> : type_is< T > {}; // 移除了 const 的相同类型
// 举一反三:下面是 移除顶层 volatile 限定符
template <typename T> struct remove_volatile: type_is< T > {}; // 没有 volatile 正常返回
template <typename T> struct remove_volatile<T volatile> : type_is< T > {}; // 移除了 volatile 的相同类型