C++模板的原理与应用

来源:互联网 发布:温州慧网网络好不好 编辑:程序博客网 时间:2024/06/03 06:13
原文地址:C++模板的原理与应用作者:砖头不离身

相信大家对模板并不陌生,模板的基本概念我想就不用多说了。大多数人包括我自己对模板的理解就是“T容器”。

请看下面的代码:

 

template<int m1int l1int t1int m2int l2int t2>

Physical<m1+m2l1+l2t1+t2operator *Physical<m1l1t1 lhsPhysical<m2l2t2rhs)

{

    return Physical<m1+m2l1+l2t1+t2>::unit * lhs.value() * rhs.value();

}

 

头一次见到template的这种用法时,我确实有点目眩。看来我们对模板的认识只是皮毛而已 J

 

在当今的程序设计中模板已经显露出它的价值来了,比如STL全部都是以模板架构的。VS2003(VC7)以来的编译器也对模板提供了更多的支持。

 

我们需要对模板有更进一步的认识,下面就让我们进入模板的世界。

 

 

  • 模板的特性

首先来看一个例子:

 

template<int n>

int Func1()    return n; }

int Func2(int n){ return n; }

 

你能看出这两个函数的区别在哪里吗?

它们的区别就在于,Func1的参数是编译期指定,如Func1<0>() ;而Func2的参数则是运行期指定。

 

这正体现了模板的特性或者说是它的技术核心,就是编译期的动态机制这种机制使程序在运行期具有更大的效率优势

到此你是不是对本文开头那段代码有了更深入的理解了呢?

 

模板的另一个特性是,如果一个模板没有被特化,那么编译器根本不会去理会它,也就是说模板内的代码被隐藏了。

 

 

  • 函数模板与类模板

这是一个函数模板:template<class T> Func(T param){}

函数模板的模板参数是隐式的,编译器会自动根据传入值的类型来确定模板参数的类型。因此函数模板的模板参数不能有默认值

 

这是一个类模板:template<class T> class MyClass {};

类模板的模板参数是显式的,使用一个模板类时必须指明其模板参数,因此类模板的模板参数可以有默认值

我们还可以做更多的事情,比如MyClass可以派生自TXTP界面库就是这么做的),在MyClass内部可以使用关于Tenumtypedef等等。这些将在下文一一谈到。

 

(我在这里提出一个建议,创建一个类模板,请记得第一件事就是对模板参数进行typedef定义。)

 

 

  • 模板的部分特化与应用

模板最有价值的地方就是它的部分特化,也是应用最广泛的特性。

所谓“部分特化”也就是说,一个模板有多个参数,但我们只对其中一部分参数进行特化,或者是只针对常量模板参数的某种情况进行特化。

 


编译期ASSERT

这是对bool型模板参数部分特化的一个例子。最简单的实现:

 

template<boolstruct CompileTimeAssert;

template<> struct CompileTimeAssert<true>{};

 

当我们将一个表达式作为模板参数,而这个表达式的值为false时,编译器就找不到合适的实现,便会报错了。

 

我们可以将它扩展一下:

template<boolstruct CompileTimeAssert CompileTimeAssert(…);};  // 这里使用了C++支持的任意参数表

template<> struct CompileTimeAssert<false>{};

 

#define COMPILE_CHECK(exprmsg)/

{/

           class ERROR_##msg {};/

           (voidCompileTimeAssert<((expr)!=0)>(ERROR_##msg());/

}

   // 这里不直接传入expr是为了获得更大的适应性,因为expr有可能是无法隐式转换为bool型的(比如指针),笔者曾参与的

  // 一个项目在由VC6平台升级到VS2003平台时,由于后者ASSERT宏实现的变化,就遇到了这一问题。

 

当然,事实是编译期可用的表达式或函数(如sizeof__alignof数量上并不太多,但是这种方式是有着积极意义的。

 

编译期分派与类型选择

 

常量映射为类型

请看这样一个模板:template<int vstruct Int2Typeenumvalue v };};

模板参数的不同数值,就会产生不同类型的Int2Type。即Int2Type<0>不同于Int2Type<1>,以此类推。

 

我们可以利用这个模板实现编译期分派。看这样一个例子:

 

template<class T, bool bPolymorphic>

class MyClass

{

    ……

    void Func(T* pObj)

{

    if (bPolymorphic)

{

   T* pNewObj pObj->Clone();  // 多态类型

……

}

else

{

   T* pNewObj new T(*pObj);  // 非多态类型

……

}

}

};

 

显然,编译器不会让你侥幸成功。而使用了

0 0