banner.gif adie's blog
主页 博客 胭脂泪,相留醉,几时重,自是人生长恨水长东
统计
日志总数: 126
评论总数: 122
日志分类
日志归档
最近日志
最近评论
订阅
rss2.gif

atom.gif

google_rss
yc.gif 【技术资料】 阅读 5719 次

C++模板元编程(一):元编程与运行时编程的对比

2006-10-21 00:11:23
    废话就不多说了,进入正文。

一 函数

   1.函数的声明

     普通函数: int square(int x, int y);
     元函数:   template struct Square;

   2.函数的定义

    运行时函数的典型例子如:

        int square(int x, int y)
        {
           return x * x + y * y;
        }

    对应的元函数为:

        template
        struct Squart{
          enum{
            Value = x * x + y * y
          };
        };

    元函数都是用模板类来定义的,模板参数对应函数参数,在类里面定义常量或类型等来返回结果。通常我们使用 Value 来表示整数结果(通常用 enum 来定义),用 Result(通常用 typedef 来定义) 来表示类型结果。

   3.函数的调用
     运行时函数调用时的实参可以时常量,也可以时一个变量。而元函数的实参必须时编译期常量。

     运行时函数调用例子如:

       std::cout<
     元函数的调用方法:

       std::cout<::Value<
   4. 函数的参数

     运行时函数的参数不必多说,元函数可以使用的参数有:

     (1). 整数类,包括 char, short, int, long,指针等等。可以时有符号的,也可以时无符号的。

     (2). typename, 类型,包括基本类型以及自定义类型。

     (3). class, 在 C++ 中的定义与上同。但我们使用的时候通常只用于那些只表示自定义类型,不包括基本类型的情况。

     (4). 回调元函数,和运行时函数可以接受一个函数指针来进行回调一样,元函数的参数也可以是另外一个元函数,以此来实现元函数的回调。文字本非我所长,还是用例子来说明吧。

      先来回顾一下运行时情况是如何使用回调函数的:

      两个用于回调的函数如

      int add(int x, int y)
      {
         return x + y;
      }

      int mul(int x, int y)
      {
         return x * y;
      }
      
      使用回调函数指针的函数

      int run(int x, int y, int z, int (*manip)(int, int))
      {
         return manip(x, manip(y, z));
      }

      调用方法

      run(10, 10, 10, add);
      run(10, 10, 10, mul);

      好了,现在我们用模板元函数来完成和上面一样的功能:

      两个用于回调的函数

      template
      struct Add{
        enum{
          Value = x + y
        };
      };

      template
      struct Mul{
        enum{
          Value = x * y
        };
      };

      使用回调函数模板的元函数

      template class Manip>
      struct Run{
        enum{
          Value = Manip::Value>::Value
        };
      };

      最后调用的情况

      Run<10, 10, 10, Add>::Value
      Run<10, 10, 10, Mul>::Value

      对比以上两种情况,可以发现他们几乎是相同的。看看函数指针和被称为模板模板参数的元函数指针:
      int (*manip)(int, int)
      template class Manip
      它们也是如此的相似。

PS.以上为了表现普通函数和元函数的共同之处,建立元函数的概念故使用在两者都能使用的整数作为例子。在实际应用中元函数操作的通常是类型,而不是编译期整数。另外对其差别也未曾细述,不过只要牢记元函数只由编译器来解释执行,不会在运行期留下任何痕迹,不能使用运行期的任何东西,其差别就不难把握。


二 结构化

    结构化程序所要求的 3 大结构:顺序、选择、循环在元编程中都能够实现,但其使用起来就不象元函数那么漂亮了,下面依次说明。

    1. 顺序结构

      顺序结构是最简单的结构,其实现也很简单,举例就看明白了:

      template
      struct Max{
        private:
          enum{
            temp = x > y ? x : y
          };
        public:
          enum{
            Value = temp > z ? temp : z
          };
      };

      唯一需要注意下的就是最好能够吧中间的计算过程放在 private 区,只把要返回的内容放在 public 区。

    2. 选择结构

      元编程里的语句有限(基本上就是 enum 和 typedef, 有时也可以使用类的静态变量或静态函数等等),没有运行时的 if-else, switch-case 语句。在元编程里实现选择结构时利用 C++ 提供的模板偏特化(这里把全特化视为一种特殊的偏特化,不另外指明了)机制来模拟的。下面我们以实现取绝对值的函数来说明。当然这个例子其实可以用 x < 0 ? -x : x 一个表达式来完成的,但对于需要操作类型的元函数来说是无法使用 ? : 表达式的,本例旨在说明选择结构。

      先来看按普通选择结构的实现:

      int abs(int x)
      {
         if(x < 0)
            return -x;
         else
            return x;
      }

      下面是元编程使用模板偏特化的方法实现:

      template struct AbsImp;

      template
      struct AbsImp{
        enum{
          Value = -x
        };
      };

      template
      struct AbsImp{
        enum{
          Value = x
        };
      };

      template
      struct Abs{
        enum{
         Value = AbsImp::Value
        };
      };

      这个样子确实不怎么漂亮啊,不过我们可以写一个模板来简化:
      
      template struct if_else;

      template
      struct if_else{
        enum{
          Value = ifv
        };
      };

      template
      struct if_else{
        enum{
          Value = elsev
        };
      };

      有了以上这个模板我们的 Abs 函数可以简化为:

      template
      struct Abs{
        enum{
          Value = if_else::Value
        };
      };

      上面的 if_else 模板看上去和 ? : 表达式一样,关键在于稍加修改就可以让它用于操作类型。 if_else 模板是可以通用的,只需要写一次,而且更棒的是,Loki, boost::mpl 等库中已经为我们提供了类似的 if, switch 模板函数了。

    2. 循环结构

      元编程中也没有直接的循环结构语句,要实现循环结构是靠递归和选择结构来模拟的。我们知道,任何递归都可以用循环来代替的。其实,任何循环也可以用递归来代替,前提是你有足够的堆栈来支持足够的递归曾数。编译器支持的元函数递归层数是有限制的,所以模拟出来的循环结构的循环次数也是有限制的。

      先来看看运行期函数如何用递归和选择来实现循环,例如要实现如下的循环:

      int f()
      {
        int num = 0;
        for(int i = 0; i < 10; ++i)
        {
           num = num + i;
        }
        return num;
      }

      如果用递归来模拟的话:

      int recuf(int num, int i)
      {
         if(i == 10)
           return num;
         else
           return recuf(num, i+1) + i;
      }

      int f()
      {
        return recuf(0, 0);
      }

      根据上面的递归方法我们可以对比元编程中的方法:

      template struct metaF;

      template
      struct metaF{
        enum{
          Value = num
        };
      };  // if(i == 10) return num

      template
      struct metaF{
        enum{
          Value = metaF::Value + i
        };
      }; // else return refcuf(num, i+1) + i

      利用偏特化模拟了 if - else, 在 Value = metaF::Value + i 中使用递归,最后完成了和循环一样的功能。


三 库

   有了函数,有了顺序、选择、循环结构,我们可以做很多事情了。不过和传统编程一样,我们还需要库来提供一些基本功能,避免重复造车轮的工作。标准模板库中的某些类或许对元编程有些用处,但他们不是为元编程设计的,功能远远不够。目前元编程方面的库还远不能和传统编程的库相比,似乎只有 boost::mpl 及一些相关组件可供使用。

▲评论

X 正在回复:
姓 名: 留下更多信息
性 别:
邮 件:
主 页:
Q Q:
来 自:
职 业:
评 论:
验 证:


Valid HTML 4.01 Strict Valid CSS!
Copyleft.A!die Software Studio.ADSS
Power by webmaster@adintr.com