初探模板元编程

关于模板元编程的知识也有所了解,相关的书籍也看过几本,但是至今还没有亲手写过一个模板元程序,原因就是没有一个合适的机会应用模板元编程技术,今天在CSDN上看见一个帖子,定义常量字符串

char *p="Hello,Word!";

既然是常量字符串,应该可以在编译期知道p的长度,在编译期间如何得到?

这个问题,我首先想到用模板元编程来实现,于是尝试编写一个。

真正用模板元编程时,才发现自己完全没有思路,不知道如何解决。于是下定决心学好模板元编程,这个问题就是我学习的动力,希望以后能够使用模板元编程解决这个问题,或者明确下来不能解决这个问题。

今天写了个例子,才发现自己对模板元的理解很肤浅,常常被一些弱智的问题打败,对于任何想新学模板元编程的同学可能都会碰到,记录下来和大家分享一下,希望对别人有所帮助。

我们从模板元编程的"Hello word"开始,使用模板元编程求一个数的阶乘。这是我写的第一个例子

#include
 
 
  
  
using namespace::std;
template
  
  
   
   
class B
{
    public:
        int num()
        {
            return I * B
   
   
    
    ().num();
        }
};
template<>
class B <1>{
    public:
        int num()
        {
            return 1;
        }
};

int main()
{
	int i = B<5>().num();
	cout << i << endl;
}

   
   
  
  
 
 
编译运行,输出正确结果,啊哈,这么简单。但是很遗憾,这不是一个模板元编程,而是一个泛型编程而已。模板元编程是运用编译器在编译阶段程序设计,而上面是类实例,通过调用对象方法来实现的,大忌啊!所以要仔细区分编译期和运行期的区别,下面有必要列举 C++ 中常用编译期操作和运行期操作。
编译期

typedef影射
static类型变量和函数
const 类型变量
= :? -运算符
enum
运行期
对象使用
函数调用
变量赋值
操作变量时 &,+=,++,-- 等运算符。

所以,如果想实现模板元编程,必须要把握的是一定要在编译期完成程序,而不是在程序的运行期,仔细区分运行期和编译期是模板元编程的第一步。

那么根据以上要求,一种比较正确的方法是,在下面的例子中融合了enumconst static组合两种方法

template
 
 
  
  
class B
{
    public:
        enum {value=I*B
  
  
   
   ::value};
        static const int value1=I*B
   
   
    
    ::value1;
};
template<>
class B <1>{
    public:
        enum {value=1};
        static const int value1=1;
};
 
int main()
{
    int a[B<5>::value];
    int b[B<5>::value1];
    int i = B<5>::value;
    int j = B<5>::value1;
    cout << i << endl;
    cout << j << endl;
}

   
   
  
  
 
 
这里定义 a,b 数组,是为了验证 B<5>::value B<5>::value1(value1是采用静态常量的方式,value是枚举类型,两者的区别可以理解为静态常量占用运行期内存空间,而枚举类型不会) 编译后是常量(对 windows 平台有效),当然也可以通过查看汇编代码来发现,下面是我们上面这段代码的关键汇编代码
main:
.LFB1442:
    pushl   %ebp
.LCFI4:
    movl    %esp, %ebp
.LCFI5:
    subl    $984, %esp
.LCFI6:
    andl    $-16, %esp
    movl    $0, %eax
    addl    $15, %eax
    addl    $15, %eax
    shrl    $4, %eax
    sall    $4, %eax
    subl    %eax, %esp
    movl    $120, -972(%ebp) 注释1
    movl    $120, -976(%ebp) 注释2
    subl    $8, %esp
    pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    subl    $12, %esp
    pushl   -972(%ebp)
    pushl   $_ZSt4cout
.LCFI7:

上述代码中注释1int i = B<5>::value;的汇编代码,注释2int i = B<5>::value1;得汇编代码,通过上述汇编代码,我们看到编译后,B<5>::value或者B<5>::value1直接就为120,没有经过任何函数调用,作为对比,我们看看第一个例子的汇编代码

main:
.LFB1446:
    pushl   %ebp
.LCFI4:
    movl    %esp, %ebp
.LCFI5:
    subl    $8, %esp
.LCFI6:
    andl    $-16, %esp
    movl    $0, %eax
    addl    $15, %eax
    addl    $15, %eax
    shrl    $4, %eax
    sall    $4, %eax
    subl    %eax, %esp
    subl    $12, %esp
    leal    -5(%ebp), %eax
    pushl   %eax
.LCFI7:
    call    _ZN1BILi5EE3numEv 注释1
    addl    $16, %esp 注释2
    movl    %eax, -4(%ebp) 注释3
    subl    $8, %esp
    pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    subl    $12, %esp
    pushl   -4(%ebp)

注释1,注释2,注释3即为int i = B<5>().num();的汇编代码,我们发现它首先进行的是一个函数调用,之后执行一个加操作,把结果放到%eax寄存器中,然后赋值给变量i,正如注释3所示。本来在上述代码中,如果我们这样声明

int a[B<5>().num()];

欲在栈上申请B<5>().num()大小空间,如果B<5>().num()是常量我们可以这样使用,为变量的话我们需要

int *a = new int[B<5>().num()];

而当我编写

int a[B<5>().num()];

时,我在g++ 3.4Linux)和g++2.9FreeBSD)下编译居然都没有错误,看来g++编译器对此作了扩展,声明数组的方法,在Unix系统不是正确验证编译常量的方法,最好方式还是看汇编代码。

看来我刚开始要设计的两种方案

方案1

template
 
 
  
  
class S{
    public:
        enum {value=1+S
  
  
   
   ::value}
};
 
template<>
class S
   
   
    
    {
    public:
        enum {value=1};
};

   
   
  
  
 
 
方案2
template
 
 
  
  
class S{
    public:
        enum {value=1+S<*((const char *)&p+1)>::value}
};
 
template<>
class S<'/0'>{
    public:
        enum {value=1};
};

 
 

都失败了,在方案1中模板类型不能是const char*,模板类型只能为类类型,或者类整形类型,比如bool,int,unsigned int,double,short等,方案2&*不能用在常量表达式上。

尝试使用常量方式解决编译期间统计字符串长度的方法到此为止了,看来要和类类型结合使用,才有可能解决这个问题,未完待续。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值