漫漫C++路
C++是一种两层次语言。
C++的模板设计之初是为了实现泛型编程,但后来发现模板的能力远不止泛型编程,C++模板是图灵完备的,理论上来说C++模板可以执行任何计算任务,但因为模板是编译期间计算的,其能力受编译器实现的限制,因而衍生出模板元编程。
动态代码
C++执行期间的代码称为动态代码,动态代码就不多介绍。
静态代码
C++执行编译计算的代码称为静态代码,静态代码由模板元编程和预处理的宏组成,预处理的宏也能进行部分编程,因而称为宏元编程。
模版元编程举例:
template<typename T, int i=1>
class someComputing {
public:
typedef volatile T* retType; // 类型计算
enum { retValume = i + someComputing<T, i-1>::retValume }; // 数值计算,递归
static void f() { std::cout << "someComputing: i=" << i << '\n'; }
};
template<typename T> // 模板特例,递归终止条件
class someComputing<T, 0> {
public:
enum { retValume = 0 };
};
模版元编程的控制结构:
条件 if 语句:
// 通例为空,若不匹配特例将报错,很好的调试手段(这里是 bool 就无所谓了)
template<bool c, typename Then, typename Else> class IF_ { };
template<typename Then, typename Else>
class IF_<true, Then, Else> { public: typedef Then reType; };
template<typename Then, typename Else>
class IF_<false,Then, Else> { public: typedef Else reType; };
IF_<> 的使用示例
const int len = 4;
typedef
IF_<sizeof(short)==len, short,
IF_<sizeof(int)==len, int,
IF_<sizeof(long)==len, long,
IF_<sizeof(long long)==len, long long,
void>::reType>::reType>::reType>::reType
int_my; // 定义一个指定字节数的类型
std::cout << sizeof(int_my) << '\n';
while 语句:
// 隐含要求: Condition 返回值 ret,Statement 有类型 Next
template<template<typename> class Condition, typename Statement>
class WHILE_ {
template<typename Statement> class STOP { public: typedef Statement reType; };
public:
typedef typename
IF_<Condition<Statement>::ret,
WHILE_<Condition, typename Statement::Next>,
STOP<Statement>>::reType::reType
reType;
};
WHILE_<> 的使用示例
// 计算 1^e+2^e+...+n^e
template<int n, int e>
class sum_pow {
template<int i, int e> class pow_e{ public: enum{ ret=i*pow_e<i,e-1>::ret }; };
template<int i> class pow_e<i,0>{ public: enum{ ret=1 }; };
// 计算 i^e,嵌套类使得能够定义嵌套模板元函数,private 访问控制隐藏实现细节
template<int i> class pow{ public: enum{ ret=pow_e<i,e>::ret }; };
template<typename stat>
class cond { public: enum{ ret=(stat::ri<=n) }; };
template<int i, int sum>
class stat { public: typedef stat<i+1, sum+pow<i>::ret> Next;
enum{ ri=i, ret=sum }; };
public:
enum{ ret = WHILE_<cond, stat<1,0>>::reType::ret };
};
int main() {
std::cout << sum_pow<10, 2>::ret << '\n';
std::cin.get(); return 0;
}
借鉴总结
- C++ 模板包括函数模板和类模板,模板参数形式有:类型、模板型、非类型(整型、指针);
- 模板的特例化分完全特例化和部分特例化,实例将匹配参数集合最小的特例;
- 用实例参数替换模板形式参数称为实例化,实例化的结果是产生具体类型(类模板)或函数(函数模板),同一模板实参完全等价将产生等价的实例类型或函数;
- 模板一般在头文件中定义,可能被包含多次,编译和链接时会消除等价模板实例;
- template、typename、this 关键字用来消除歧义,避免编译错误或产生不符预期的结果;
- C++11 对模板引入了新特性:“>>”、函数模板也可以有默认参数、变长模板参数、外部模板实例(extern),并弃用 export template;
- C++ 模板是图灵完备的,模板编程是函数编程风格,特点是:没有可变的存储、递归,以“<>”为输入,typedef 或静态常量为输出;
- 编译期数值计算虽然实际意义不大,但可以很好证明 C++ 模板的能力,可以用模板实现类似普通程序中的 if 和 while 语句;
- 一个实际应用是循环展开,虽然编译器可以自动循环展开,但我们可以让这一切更可控;
- C++ 模板编程的两个问题是:难调试,会产生冗长且难以阅读的编译错误信息、代码膨胀(源代码膨胀、二进制对象文件膨胀),改进的方法是:增加一些检查代码,让编译器及时报错,使用特性、策略等让模板更通用,可能的话合并一些模板实例(如将代码提出去做成单独模板);
- 表达式模板和向量计算是另一个可加速程序的例子,它们将计算表达式编码到类型,这是通过模板嵌套参数实现的;
- 特性,策略,标签是模板编程常用技巧,它们可以是模板变得更加通用;
- 模板甚至可以获得类型的内部信息(是否有某个 typedef),这是反射中的内省,C++ 在语言层面对反射支持很少(typeid),这不利于模板元编程;
- 可以用递归实现伪变长参数模板,C++11 变长参数模板背后的原理也是模板递归;
- 元容器存储元信息(如类型)、类型过滤过滤某些类型,它们是元编程的高级特性。