C/C++编程:模板元编程中的迭代

比眉伴天荒 2023-01-16 03:24 217阅读 0赞

模板元编程:计算平凡根

  1. // 用于计算sqrt(N)的基本模板
  2. template <int N, int LO=0, int HI=N>
  3. class Sqrt{
  4. public:
  5. // 计算中点
  6. enum {
  7. mid = (LO + HI + 1) / 2};
  8. //借助二分查找一个较小的result
  9. enum {
  10. result = (N < mid*mid) ? Sqrt<N, LO, mid - 1> ::result : Sqrt<N, mid, HI>::result};
  11. };
  12. // 局部特化:适用于L0等于HI
  13. template <int N, int M>
  14. class Sqrt<N, M, M>{
  15. public:
  16. enum {
  17. result = M};
  18. };

第一个模板是一个普通的递归计算:运用模板参数N(用于计算平方根的值)和两个(其他的)可选参数进行调用:其中可选参数表示结果可能的最大值和最小值。于是,如果只使用一个实参N来调用模板参数的话,那么我们知道所求的平方根的值域必定在0和N之间

用于结束递归的特化的适用条件是:LO和HI具有相同的值,其中M就是我们的最终结果。

  1. #include <iostream>
  2. int main(){
  3. printf("%d", Sqrt<16>::result);
  4. }

Sqrt<16>::result被扩展为Sqrt<16, 1, 16>::result
在模板的内部,该模板元计算Sqrt<16, 1, 16>::result的过程如下:
在这里插入图片描述
接下来要计算Sqrt<16, 1, 8>::result,过程如下:
在这里插入图片描述
接下来要计算Sqrt<16, 1, 4>::result,过程如下:
在这里插入图片描述
接下来要计算Sqrt<16, 3, 4>::result,过程如下:
在这里插入图片描述
于是Sqrt<16, 3, 4>::result结束了整个递归过程,因为它的上界等于下界,能够与显式特化进行匹配。因此,最终的结果如下:result = 4

追踪所有的实例化

在上面的例子中,我们给出了计算16的平方根的一系列重要的过程。然而,当编译器试图计算下面表达式时:
在这里插入图片描述
编译器会实例化Sqrt<16, 1, 8>Sqrt<16, 9, 16>。而且,由于代码试图使用::运算符访问结果类的成员(即result),所以类中的所有成员同时也会被实例化。这就意味着,实例化Sqrt<16, 9, 16>将会使得Sqrt<16, 9, 12>Sqrt<16, 16, 16>完全实例化。

这并不是我们所期望的,因为对于大多数编译器而言,模板实例化通常都是代价高昂的(事实上,C++标准建议最多只进行17层递归特化)。幸运的是,存在一些限制实例化数量过于庞大的技术。比如同使用特化来选择计算的结果而非使用?:

  1. template<bool C, typename Ta, typename Tb>
  2. class IfThenElse;
  3. // partial specialization: true yields second argument
  4. template<typename Ta, typename Tb>
  5. class IfThenElse<true, Ta, Tb> {
  6. public:
  7. typedef Ta ResultT;
  8. };
  9. // partial specialization: false yields third argument
  10. template<typename Ta, typename Tb>
  11. class IfThenElse<false, Ta, Tb> {
  12. public:
  13. typedef Tb ResultT;
  14. };
  15. template<int N, int LO=1, int HI=N>
  16. class Sqrt {
  17. public:
  18. // compute the midpoint, rounded up
  19. enum {
  20. mid = (LO+HI+1)/2 };
  21. // 使用二分法查找一个较小的值
  22. typedef typename IfThenElse<(N<mid*mid),
  23. Sqrt<N,LO,mid-1>,
  24. Sqrt<N,mid,HI> >::ResultT
  25. SubT;
  26. enum {
  27. result = SubT::result };
  28. };
  29. // 用于结束递归的局部特化
  30. template<int N, int S>
  31. class Sqrt<N, S, S> {
  32. public:
  33. enum {
  34. result = S };
  35. };

使用归纳变量

上面实现可能关于复杂。如果我们用普通的C++程序计算平方根一般会这样写:

  1. ing I;
  2. for(I = 0; I*I < N; ++I){
  3. ;
  4. }

然而,作为一个元编程,我们需要以递归的方式来组织这个迭代,而且我们需要一个终止条件来结束该递归:

  1. // 借助于迭代计算sqrt(N)的基本模板
  2. template<int N, int I=0>
  3. class Sqrt{
  4. public:
  5. enum{
  6. result = (I*I<N) ? Sqrt<N, I+1>::result : I};
  7. };
  8. //用于结束迭代的局部特化
  9. template<int N>
  10. class Sqrt<N, N>{
  11. public:
  12. enum{
  13. result = N};
  14. };

同样,我们可以使用特化来限制实例化个数:

  1. template<bool C, typename Ta, typename Tb>
  2. class IfThenElse;
  3. // partial specialization: true yields second argument
  4. template<typename Ta, typename Tb>
  5. class IfThenElse<true, Ta, Tb> {
  6. public:
  7. typedef Ta ResultT;
  8. };
  9. // partial specialization: false yields third argument
  10. template<typename Ta, typename Tb>
  11. class IfThenElse<false, Ta, Tb> {
  12. public:
  13. typedef Tb ResultT;
  14. };
  15. template <int N>
  16. class Value{
  17. public:
  18. enum {
  19. result = N};
  20. };
  21. template <int N, int I = 0>
  22. class Sqrt{
  23. public:
  24. //以实例化下一步 Sqrt<N,I+1>或者结果类型Value<I>作为两个分支
  25. typedef typename IfThenElse<(I*I<N),
  26. Sqrt<N,I+1>,
  27. Value<I>
  28. >::ResultT
  29. SubT;
  30. //使用分支类型的结果
  31. enum {
  32. result = SubT ::result};
  33. };

总结

一个模板元编程可以包含下面几个部分:

  • 状态变量:也就是模板参数
  • 迭代构造:通过递归
  • 路径选择:通过使用条件表达式或者特化
  • 整型(即枚举里面的值应该为整形)算法

发表评论

表情:
评论列表 (有 0 条评论,217人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C/C++编程

    > 迭代器在STL中起着粘合器的作用,用来将STL的各个部分结合在一起。从本质上来说,STL提供的所有算法都是模板,我们可以通过使用自己指定的迭代器对这些目标进行特化。

    相关 C/C++编程:hello 模板编程

    什么是模板元编程 模板元编程:编译系统将会执行我们所写的代码,来生成新的代码,而这些新代码才真正实现了我们所期望的功能。 通常, 模板元编程这个概念意味着一种反射的特性