C++模板的原理与应用
来源:互联网 发布:网络教育哪些学校好 编辑:程序博客网 时间:2024/05/22 12:57
相信大家对模板并不陌生,模板的基本概念我想就不用多说了。大多数人包括我自己对模板的理解就是“T容器”。
请看下面的代码:
template<intm1, int l1, int t1,int m2, int l2, intt2>
Physical<m1+m2,l1+l2, t1+t2> operator*(Physical<m1,l1, t1 > lhs, Physical<m2,l2, t2> rhs)
{
return Physical<m1+m2,l1+l2, t1+t2>::unit*lhs.value() *rhs.value();
}
头一次见到template的这种用法时,我确实有点目眩。看来我们对模板的认识只是皮毛而已J
在当今的程序设计中模板已经显露出它的价值来了,比如STL全部都是以模板架构的。自VS2003(VC7)以来的编译器也对模板提供了更多的支持。
我们需要对模板有更进一步的认识,下面就让我们进入模板的世界。
§1模板的特性
首先来看一个例子:
template<intn>
intFunc1() { returnn; }
intFunc2(int n) { return n; }
你能看出这两个函数的区别在哪里吗?
它们的区别就在于,Func1的参数是编译期指定,如Func1<0>();而Func2的参数则是运行期指定。
这正体现了模板的特性或者说是它的技术核心,就是编译期的动态机制,这种机制使程序在运行期具有更大的效率优势。
到此你是不是对本文开头那段代码有了更深入的理解了呢?
模板的另一个特性是,如果一个模板没有被特化,那么编译器根本不会去理会它,也就是说模板内的代码被隐藏了。
§2函数模板与类模板
这是一个函数模板:template<class T> Func(T param) {}
函数模板的模板参数是隐式的,编译器会自动根据传入值的类型来确定模板参数的类型。因此函数模板的模板参数不能有默认值。
这是一个类模板:template<class T> class MyClass {};
类模板的模板参数是显式的,使用一个模板类时必须指明其模板参数,因此类模板的模板参数可以有默认值。
我们还可以做更多的事情,比如MyClass可以派生自T(XTP界面库就是这么做的),在MyClass内部可以使用关于T的enum、typedef等等。这些将在下文一一谈到。
(我在这里提出一个建议,创建一个类模板,请记得第一件事就是对模板参数进行typedef定义。)
§3模板的部分特化与应用
模板最有价值的地方就是它的部分特化,也是应用最广泛的特性。
所谓“部分特化”也就是说,一个模板有多个参数,但我们只对其中一部分参数进行特化,或者是只针对常量模板参数的某种情况进行特化。
?编译期ASSERT
这是对bool型模板参数部分特化的一个例子。最简单的实现:
template<bool>struct CompileTimeAssert;
template<>struct CompileTimeAssert<true> {};
当我们将一个表达式作为模板参数,而这个表达式的值为false时,编译器就找不到合适的实现,便会报错了。
我们可以将它扩展一下:
template<bool>struct CompileTimeAssert {CompileTimeAssert(…) ;}; // 这里使用了C++支持的任意参数表
template<>struct CompileTimeAssert<false> {};
#defineCOMPILE_CHECK(expr,msg) /
{/
class ERROR_##msg {}; /
(void)CompileTimeAssert<((expr)!=0)>(ERROR_##msg()); /
}
// 这里不直接传入expr是为了获得更大的适应性,因为expr有可能是无法隐式转换为bool型的(比如指针),笔者曾参与的
// 一个项目在由VC6平台升级到VS2003平台时,由于后者ASSERT宏实现的变化,就遇到了这一问题。
当然,事实是编译期可用的表达式或函数(如sizeof、__alignof)数量上并不太多,但是这种方式是有着积极意义的。
?编译期分派与类型选择
常量映射为类型
请看这样一个模板:template<intv> struct Int2Type{ enum{ value = v }; };
模板参数的不同数值,就会产生不同类型的Int2Type。即Int2Type<0>不同于Int2Type<1>,以此类推。
我们可以利用这个模板实现编译期分派。看这样一个例子:
template<class T,bool bPolymorphic>
classMyClass
{
……
void Func(T* pObj)
{
if (bPolymorphic)
{
T* pNewObj = pObj->Clone(); // 多态类型
……
}
else
{
T* pNewObj = new T(*pObj); // 非多态类型
……
}
}
};
显然,编译器不会让你侥幸成功。而使用了Int2Type就不一样了:
template<class T,bool bPolymorphic>
classMyClass
{
……
voidFunc(T* pObj)
{
Func(pObj, Int2Type< bPolymorphic >);
}
private:
void Func(T* pObj, Int2Type<true>)
{
T* pNewObj = pObj->Clone(); // 多态类型
……
}
voidFunc(T* pObj,Int2Type<false>)
{
T* pNewObj = new T(*pObj); // 非多态类型
……
}
};
类型映射为类型
也就是这样一个模板:template<typename T> struct Type2Type{typedef T _MyType; };
其用法跟Int2Type类似,但我们可以利用它进而实现类型选择。
举一个例子,我们使用vector来存储数据,如果数据是非多态类型,存储其实体比较有效率;如果数据是多态类型,显然只能存储其指针。使用Type2Type实现如下:
template<boolflag, typename T,typename U> structTypeSelector { typedef T _Result; };
template<typename T,typename U> structTypeSelector<false, T, U> {typedef U _ Result; };
template<typename T,bool bPolymorphic>
classMyClass
{
……
typedef TypeSelector< bPolymorphic, T*, T>::_Result _MyType;
}
模板的部分特化与枚举的结合使用
这是一种具有极大的可扩展性的方式。比如我们要检查两个类型是否是相同的类型,可以这样实现:
template<class T,class U> struct CompileCheck { enum{ same_type = false }; };
template<class T>struct CompileCheck<T, T> {enum{ same_type =true }; };
使用这种方式还可以检查两个类型是否可以转换、是否可以双向转换、是否具有派生关系,可以判断一个类型是否是基本类型、属于那种基本类型、是否是指针,等等。当然这些都是在编译期进行的。请大家参阅附注中说明的原著,这里就不详述了。
一个另类的技巧
代码如下:
template<class T>struct UnConst {typedef T _Result; };
template<>struct UnConst <const T> {typedef T _ Result; };
template<class T>
classMyClass
{
……
typedef UnConst <T>::_Result _MyType;
}
看,我们巧妙的去掉了const修饰符。用同样的方法我们还可以去掉volatile修饰符。
- C++模板的原理与应用
- C++模板的原理与应用
- C++模板的原理与应用
- Unity基于模板生成代码的原理与应用
- J.U.C--ThreadLocal的应用与使用原理
- J.U.C--ThreadLocal的应用与使用原理
- 标签与模板的应用
- 【Unity编辑器】Unity基于模板生成代码的原理与应用
- J.U.C-DelayQueue原理与应用
- 景深的原理与应用
- PAM的原理与应用
- PAM的原理与应用
- Ajax的原理与应用
- DS18B20的原理与应用
- 【c++】函数模板的简单应用
- PHP模板引擎的原理与实践
- PHP模板引擎的原理与实践
- 前端模板的原理与实现
- 阿狸的童话,我的梦
- Java 验证 身份证号码是否规范
- 父控件与子控件的焦点问题
- 戒骄戒躁,匍匐前移
- 一个列表页面的测试用例的组织
- C++模板的原理与应用
- 分享开发Android应用需注意的两个要点
- 存储过程-个人总结的一点东西
- POJ 1149 最大流
- fork系统调用
- 没有什么比爱你更珍贵_建峰提供伤感日志
- jdom解析XML文件基础入门篇
- Qt入门-安装
- 因为你的不在乎,让我学会了冷漠