weitom1982

向各位技术前辈学习,学习再学习.
posts - 299, comments - 79, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

STL中用到的静态分派(转)

Posted on 2006-03-21 09:28 高山流水 阅读(137) 评论(0)  编辑 收藏 引用 所属分类: 程序语言

C++的静态分派技术

Posted on 2005-11-11 11:42 nacci 阅读(394) 评论(1)  编辑 收藏收藏至365Key 所属分类: C++ Generic
我们可以用不同的整数来实例化同一个模板,从而构造不同的类型,进而在编译期执行某些抉择。


Alexandrescu 最初设计的一个简单的模版,现在成了泛型设计的常用手法:

template <int v>

struct Int2Type {

    enum { value  = v };

};

对于每一个不同的常整数, Int2Type 都代表不同的类型。这是因为不同的模版实例化都代表不同的类型,也就是说 Int2Type<0> Int2Type<1> 是完全不同的。

当你想根据编译时结果来进行某些抉择——例如选择不同的函数——时,你可以依赖一个常整数来帮你完成分派工作,这时 Int2Type 便可以帮你是实现这个方法。

一般来说,你在下面两个情况中需要使用 Int2Type

l          你需要根据编译时常量来调用不同的函数

l          你需要在编译时执行分派工作

如果是在运行时执行分派工作,你可以用 if-else switch 语句来简单的实现。在大部分的时候,这种运行时成本都是微不足道的。但是,有时它们却不能满足你的要求。既是是在编译期可以决定其分支,编译器还是会勤劳的为你编译其所有的分支,这也就意味着 if-else 的所有分支必须被成功编译。有些困惑?继续看下去:

考虑下面的情形:你设计了一个泛型容器 NiftyContainer

template <class T> class NiftyContainer {

    ...

};

NiftyContainer 容器包含指向 T 对象的指针。为了复制 NiftyContainer 中的一个对象,你可能需要调用 T 的拷贝构造函数(对于非多态类型)或者一个名为 Clone() 的虚函数(对于多态类型)。你可以通过设置一个 bool 类型的模版参数来从类的客户手里获得关于多态的信息。

template <class T, bool isPolymorphic> class NiftyContainer {

    // Other actions

    void DoSomething() {

       T* pSomeObj = ...;

       if(isPolymorphic) {

           T* pNewObj = pSomeObj->Clone();

           // Some polymorphic algorithm

       }

       else {

           T* pNewObj = new T(*pSomeObj);

           // Some non-polymorphic algorithm

       }

    }

};

问题在于编译器不会让你侥幸编译上面的代码。例如,如果一个多态类型没有定义 Clone() ,那么 NiftyContainer::DoSomething 绝对不会通过编译。尽管在编译时我们肯定可以对于分支进行判断,但这毕竟不是编译器的工作,他只会勤劳的为你编译出所有的代码。于是当你试图调用 NiftyContainer<int, false>::DoSomething 的时候,编译器还是会停在 pObj->Clone() 上,并且抱怨说:“你在做什么?”

对于非多态类型分支,也有可能发生编译错误。如果 T 是一个多态类型,并且把它的拷贝构造函数设定为 private 的时候(这时一个多态类的良好行为),非多态分支的 new T(*pObj) 就会发生错误。

你可能会想,如果编译器可以不去理会那些不必要的分支就好了,但是看来不太可能。那么,如何是好呢?

其实,方法有很多, Int2Type 提供了一个简洁的办法。它可以根据 true false 来生成两个不同的类型,而后根据 Int2Type<isPolymorphic> 评估正确的调用。

template <class T, bool isPolymorphic> class NiftyContainer {

private :

    // Other actions

    void DoSomething(T* pObj, Int2Type<true>) {

       T* pNewObj = pSomeObj->Clone();

       // Some polymorphic algorithm

    }

    void DoSomething(T* pObj, Int2Type<false>) {

       T* pNewObj = new T(*pSomeObj);

       // Some non-polymorphic algorithm

    }

public :

    void DoSomething(T* pObj) {

       DoSomething(pObj, Int2Type<isPolymorphic>());

    }

};

当你想把常整数用作一个类型的时候, Int2Type 是非常方便的。你可以传递一个临时的变量来重载函数。而之所以我们可以这样做,是因为编译器不会去编译没有用到的模板函数。

只有注册用户登录后才能发表评论。