EffectiveC++ | 48 认识 template 元编程

条款48:认识 template 元编程

EffectiveC++ | 48 认识 template 元编程_第1张图片

文章目录

  • 条款48:认识 template 元编程
    • TMP介绍
    • 回溯条款47
    • TMP 已被证明是一个图灵完备(Turing-complete)机器
    • 请记住
      • 欢迎关注公众号【三戒纪元】

TMP介绍

**template metaprogramming(TMP,模板元编程)**是编写 template C++ 程序并执行于编译期的过程。

所谓模板元程序就是:以 C++ 写成,执行于 C++ 编译器内的程序

TMP程序的输出就是从 templates 具现出来的若干 C++ 源码,然后再被编译。可以理解为对调用templates 的地方进行适配

由于 template metaprograms 执行于 C++ 编译期,因此可以将很多工作从运行期转移到编译期。这会导致2个结果:

  • 某些在运行期才能检测出来的错误,在编译器即可找出来;
  • 使用TMP的C++程序可能在每一方面都更加高效:比如较小的可执行文件,较短的运行期,较少的内存需求。

缺点也很明显,将工作移至编译期,会导致编译时间变长

回溯条款47

条款 47 讨论的traits,其实就是一个元编程的例子。将原本运行期间通过 if…else 检查的工作转移到编译器检查。

回顾一下实现一个 Advance函数的伪代码

template <typename IteratorType, typename DistT>
void Advance(IteratorType& iter, DistT d) {
  if (typeid(typename std::iterator_traits<IteratorType>::iterator_category) ==
      typeid(std::random_access_iterator_tag)) {
    iter += d;  //针对random access 迭代器使用迭代器算术运算
  } else {
    if (d >= 0) {  //针对其他迭代器类型,反复调用++或--
      while (d--) ++iter;
    } else {
      while (d++) --iter;
    }
  }
}

int main() {
  std::list<int>::iterator iter;
  Advance(iter, 10);
  return 0;
}

编译会报错

randy.cpp: In instantiation of ‘void Advance(IteratorType&, DistT) [with IteratorType = std::_List_iterator<int>; DistT = int]’:
randy.cpp:25:19:   required from here
randy.cpp:13:10: error: no match for ‘operator+=(operand types are ‘std::_List_iterator<int>’ and ‘int’)
   13 |     iter += d;  //针对random access 迭代器使用迭代器算术运算
      |     ~~~~~^~~~

问题出在list::iterator尝试使用 +=操作符,但 list::iterator 是 bidirectional 迭代器,并不支持 +=。只有 random access 迭代器才支持+=

这里你可能会好奇为什么我已经加了if…else 判断了,根本不会执行+=操作,为什么编译期还会报错?

这是因为编译器必须确保所有源码都有效,即使是不会执行的源码。

这也就是 if…else 是在程序运行期间才能判断的,但是编译期间纵然不会判断,但是编译器还是要用 list::iterator 去 if 中判断一下,保证程序能够进行,不能进行就会报错。

所以 traits-based TMP 解法, 其针对不同类型而进行的代码,被拆分为不同的函数,每个函数所使用的的操作都可施行于该函数所对付的类型。

TMP 已被证明是一个图灵完备(Turing-complete)机器

意思是,它可以计算任何事物。

使用 TMP 你可以声明变量,执行循环,编写及调用函数…但这些相对于“正常的” C++ 的实现会有很大的不同。比如:TMP 并没有循环部件,所有的循环效果都由递归(recursion)完成。

TMP的递归涉及“递归模板具现化”,因为TMP的循环并不涉及递归函数调用。

例如,利用TMP在编译期计算阶乘

template <unsigned n>
struct Factorial {
  enum { value = n * Factorial<n - 1>::value };
};

template <>
struct Factorial<0> {
  enum { value = 1 };
};

int main() {
  std::cout << Factorial<5>::value << std::endl; // 结果: 120
  std::cout << Factorial<10>::value << std::endl; // 结果: 3628800

  return 0;
}

每个 Factorial template 具现体都是一个 struct,每个 struct 都使用 enum back 声明一个名为 value 的 TMP 变量, value 用来保存当前计算所得的阶乘值。

和所有递归行为一样,我们需要一个特殊情况来结束递归。对于 TMP 而言就是使用 tmeplate的特化版本Factorial<0> 。

请记住

  1. Template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率
  2. TMP 可被用来生成基于政策选择组合(based on combination of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不合适的代码

欢迎关注公众号【三戒纪元】

EffectiveC++ | 48 认识 template 元编程_第2张图片

你可能感兴趣的:(C++,c++)