C++模板元编程
最近才稍微了解现代C++中非常重要的技术,即模板元编程,本文作为记录。
模板元编程(template metaprogramming)
C++ 模板最初是为实现泛型编程设计的,但人们发现模板的能力远远不止于那些设计的功能,一个重要的理论结论就是:C++ 模板是图灵完备的(Turing-complete),理论上说 C++ 模板可以执行任何计算任务,但实际上因为模板是编译期计算,其能力受到具体编译器实现的限制(如递归嵌套深度,C++11 要求至少1024,C++98要求至少 17)。C++模板元编程(所谓元编程,就是用来编写程序的程序)是“意外”功能,而不是设计的功能,这也是C++模板元编程语法丑陋的根源。
C++模板是图灵完备的,这使得C++成为两层次语言(two-level languages),其中,执行编译计算的代码称为静态代码(static code),执行运行期计算的代码称为动态代码(dynamic code)。
具体来说 C++ 模板可以做以下事情:
- 编译期数值计算
- 类型计算
- 代码计算(如循环展开)
其中数值计算实际不太有意义,而类型计算和代码计算可以使得代码更加通用,更加易用,性能更好(也更难阅读,更难调试,有时也会有代码膨胀问题)。
从编程范型(programming paradigm)上来说,C++模板是函数式编程(functional programming),它的主要特点是:函数调用不产生任何副作用(没有可变的存储),用递归形式实现循环结构的功能。C++模板的特例化提供了条件判断能力,而模板递归嵌套提供了循环的能力,这两点使得其具有和普通语言一样通用的能力(图灵完备性)。
#include <iostream>template<typenameT,inti=1>
classsomeComputing{
public:
typedefvolatileT*retType;// 类型计算
enum{retValume=i+someComputing<T,i-1>::retValume};// 数值计算,递归
staticvoidf(){std::cout<<"someComputing: i="<<i<<'\n';}
};
template<typenameT>// 模板特例,递归终止条件
classsomeComputing<T,0>{
public:
enum{retValume=0};
};
template<typenameT>
classcodeComputing{
public:
staticvoidf(){T::f();}// 根据类型调用函数,代码计算
};
intmain(){
someComputing<int>::retTypea=0;
std::cout<<sizeof(a)<<'\n';// 64-bit 程序指针
// VS2013 默认最大递归深度500,GCC4.8 默认最大递归深度900(-ftemplate-depth=n)
std::cout<<someComputing<int,500>::retValume<<'\n';// 1+2+...+500
codeComputing<someComputing<int,99>>::f();
std::cin.get();return0;
}
特性(traits)
先说作用:当函数,类或者一些封装的通用算法中的某些部分会因为数据类型不同而导致处理或逻辑不同(而我们又不希望因为数据类型的差异而修改算法本身的封装时),traits会是一种很好的解决方案。
模板特化(specialization),如下段代码使得TraitHelper<void>::isVoid
的值为真,进而可以在编译器进行类型运算。
template<typenameT>structTraitHelper{
staticconstboolisVoid=false;
};
template<>
structTraitHelper<void>{
staticconstboolisVoid=true;
}
偏特化(partial specialization),下面的例子使得输入为指针类型时返回值为真
template<typenameT>structTraitHelper{
staticconstboolisPointer=false;
};
template<typenameT>
structTraitHelper<T*>{
staticconstboolisPointer=true;
}
注意typename
和class
的区别,当要用到类型时一定要加typename
声明
template<typenameT>typenameT::value_typetest(constT&c);
实现不同类型参数传递,及不同返回类型
template<typenameT>classTest{
public:
TraitsHelper<T>::ret_typeCompute(TraitsHelper<T>::par_typed);
private:
TmData;
};
实现不同算法
template<boolb>structalgorithm_selector{
template<typenameT>
staticvoidimplementation(T&object)
{
//implement the alorithm operating on "object" here
}
};
template<>
structalgorithm_selector<true>{
template<typenameT>
staticvoidimplementation(T&object){
object.optimised_implementation();
}
};
template<typenameT>
voidalgorithm(T&object){
algorithm_selector<supports_optimised_implementation<T>::value>::implementation(object);
}
classObjectB{
public:
voidoptimised_implementation(){
//...
}
};
template<>
structsupports_optimised_implementation<ObjectB>{
staticconstboolvalue=true;
};
intmain(intargc,char*argv[]){
ObjectAa;
algorithm(a);
// calls default implementation
ObjectBb;
algorithm(b);
// calls
// ObjectB::optimised_implementation();
return0;
}
特性类(traits class)
template<classIterT>structmy_iterator_traits{
typedeftypenameIterT::value_typevalue_type;
};
my_iterator_traits<vector<int>::iterator>::value_typea;
模板特化
谓模板特例化即对于通例中的某种或某些情况做单独专门实现,最简单的情况是对每个模板参数指定一个具体值,这成为完全特例化(full specialization),另外,可以限制模板参数在一个范围取值或满足一定关系等,这称为部分特例化(partial specialization),用数学上集合的概念,通例模板参数所有可取的值组合构成全集U,完全特例化对U中某个元素进行专门定义,部分特例化对U的某个真子集进行专门定义。
// 实现一个向量类template<typenameT,intN>
classVec{
T_v[N];
// ... // 模板通例(primary template),具体实现
};
template<>
classVec<float,4>{
float_v[4];
// ... // 对 Vec<float, 4> 进行专门实现,如利用向量指令进行加速
};
template<intN>
classVec<bool,N>{
char_v[(N+sizeof(char)-1)/sizeof(char)];
// ... // 对 Vec<bool, N> 进行专门实现,如用一个比特位表示一个bool
};
enable_if
enable_if
常用于需要根据不同类型的条件实例化不同模板的时候
template<bool,classT=void>structenable_if
{};
template<classT>
structenable_if<true,T>
{
typedefTtype;
};
“替代失败不是错误”原则(Substitution Failure Is Not An Error, SFINAE)
编译期多态
奇异递归模板(curiously recurring template pattern, CRTP)
#include <iostream>usingnamespacestd;template<typenameChild>
structBase
{
voidinterface()
{
static_cast<Child*>(this)->implementation();
}
};
structDerived:Base<Derived>
{
voidimplementation()
{
cerr<<"Derived implementation\n";
}
};
intmain()
{
Derivedd;
d.interface();// Prints "Derived implementation"
}
Duck Typing
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. — James Whitcomb Riley
template<typenameT>voidf(constT&object)
{
object.f(0);// 要求类型 T 必须有一个可让此语句编译通过的函数。
}
structC1
{
voidf(int);
};
structC2
{
intf(char);
};
structC3
{
intf(unsignedshort,boolisValid=true);
};
structC4
{
Foo*f(Object*);
};
一些实际的例子
- rapidjson, https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/document.h
- Factory pattern, https://gist.github.com/sacko87/3359911
参考资料
- C++模板元编程(C++ template metaprogramming),http://www.cnblogs.com/liangliangh/p/4219879.html
- 【C++模版之旅】神奇的Traits,https://blog.csdn.net/my_business/article/details/7891687
- An introduction to C++ Traits, https://accu.org/index.php/journals/442
- C++ enable_if的使用,http://www.fuzihao.org/blog/2016/07/14/C-enable-if%E7%9A%84%E4%BD%BF%E7%94%A8/
- Polymorphism Without Virtual Functions, https://stackoverflow.com/questions/48550968/polymorphism-without-virtual-functions
- C++ Runtime Polymorphism without Virtual Functions, https://www.codeproject.com/Articles/603818/Cplusplus-Runtime-Polymorphism-without-Virtual-Fun
- The cost of dynamic (virtual calls) vs. static (CRTP) dispatch in C++, https://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual-calls-vs-static-crtp-dispatch-in-c/
- The Curiously Recurring Template Pattern in C++, https://eli.thegreenplace.net/2011/05/17/the-curiously-recurring-template-pattern-in-c/
- C++ is Lazy: CRTP, http://www.modernescpp.com/index.php/c-is-still-lazy
- Combining Static and Dynamic Polymorphism with C++ Mixin classes, https://michael-afanasiev.github.io/2016/08/03/Combining-Static-and-Dynamic-Polymorphism-with-C++-Template-Mixins.html
以上是 C++模板元编程 的全部内容, 来源链接: utcz.com/a/127825.html