设为首页 加入收藏

TOP

19.9 boost::polymorphic_cast
2013-10-07 15:07:13 来源: 作者: 【 】 浏览:73
Tags:19.9 boost::polymorphic_cast

19.9  boost::polymorphic_cast

通常情况下强制并不一定得像接口强制那样复杂。事实上,通常我们遇到的强制与接口强制相比要简单得多。

在[Stro1997]中,Bjarne Stroustrup给读者出了一道练习题,让读者去实现一个模板形式的ptr_cast,要求是它必须像dynamic_cast那样工作,但与dynamic_cast不同的是,如果转换失败的话,它对指针和引用类型同样都可能抛出bad_cast异常。Bjarne Stroustrup实际上等于是在说"编写一个模板ptr_cast,它像dynamic_cast那样工作,不同的是,如果转换失败,它会抛出bad_cast异常,而不是返回0。"除了其名字"ptr_cast"之外,如果注意一下它的其他方面,你同样会发现,ptr_cast只能对指针进行强制,不过毫无疑问这正是对它通常被采用的含义的正确解释。Boost中的polymorphic_cast的实现如下:

  1. template <class Target, class Source> 
  2. inline Target polymorphic_cast(Source* x)  
  3. {  
  4.   Target tmp = dynamic_cast<Target>(x);  
  5.   if ( tmp == 0 ) throw std::bad_cast();  
  6.   return tmp;  
  7. }  

它的使用方式如下:
  1. try  
  2. {  
  3.   Base    *b = new Base();  
  4.   Derived *d = boost::polymorphic_cast<Derived*>(a);  
  5. }  
  6. catch(std::bad_cast &x)  
  7. {  
  8.   . . .  
  9. }  

polymorphic_cast将它的形参x声明为一个指针的做法(即x的类型是Source*,而并非Source或Source&)乍一看比较奇怪,毕竟编译器能够推导出参数类型。然而实际上这样做是有目的的,因为一旦将x显式声明为指针,该强制就被限制为不能被用在除指针之外的其他类型上。

如果我们采取一个更为严格的解释,也就是说,ptr_cast既可以被用在引用上又可以被用在指针上,两种情况下,当转换失败时,它都会抛出bad_cast异常,这样一来,事情又会发生怎样的变化呢?我在网上快速地搜索了一遍,结果只发现非常少的ptr_cast,因此我猜测这个问题肯定非常棘手。对此我所能提出的最好的解决方案是在一个早晨--经过了非常艰苦的思考之后--才想出来的,如程序清单19.20所示:

程序清单19.20

  1. template <typename T> 
  2. struct ptr_cast  
  3. {  
  4. public:  
  5.   typedef typename base_type_traits<T>::cv_type cv_type;  
  6.   typedef cv_type                                     &reference;  
  7.   typedef cv_type                                     *pointer;  
  8. public:  
  9.   template <typename Source> 
  10.   ptr_cast(Source &s)  
  11.     : m_p(&dynamic_cast<Target>(s))  
  12.   {  
  13.     // 什么也不做,因为dynamic_cast对引用类型转换失败会抛出异常  
  14.   }  
  15.   template <typename Source> 
  16.   ptr_cast(Source *s)  
  17.     : m_p(dynamic_cast<Target>(s))  
  18.   {  
  19.     if(NULL == m_p)  
  20.     {  
  21.       throw std::bad_cast();  
  22.     }  
  23.   }  
  24.   ptr_cast(pointer_type pt)  
  25.     : m_p(pt)  
  26.   {}  
  27.   ptr_cast(reference_type t)  
  28.     : m_p(&t)  
  29.   {}  
  30. public:  
  31.   operator reference () const  
  32.   {  
  33.     return const_cast<reference>(*m_p);  
  34.   }  
  35.   operator pointer () const  
  36.   {  
  37.     return const_cast<pointer>(m_p);  
  38.   }  
  39. protected:  
  40.   pointer m_p;  
  41. };  

ptr_cast使用base_type_traits模板(见19.7节)来推导出恰当的带cv限定的基类型(base type)。该类型然后被用来合成对应的指针类型和引用类型,而后两者又被用来定义两个对应的隐式转换操作符,它们负责将转换后的值返回给调用方。这个问题的最后一个环节是,dynamic_cast用在指针身上的返回值会被测试是否为NULL,如果dynamic_cast转换失败从而返回的是NULL的话,就会导致抛出一个bad_cast异常。

现在,我们可以将它用于指针和引用上了。

  1. class X  
  2. {};  
  3. class D  
  4. {};  
  5. D d;  
  6. dynamic_cast<X&>(d);  // 抛出bad_cast  
  7. ptr_cast<X&>(d);       // 抛出bad_cast  
  8. dynamic_cast<X*>(&d); // 返回NULL  
  9. ptr_cast<X*>(&d);      // 抛出bad_cast  

该实现只在那些支持模板局部特化的编译器上可以工作,这样,Visual C++(www.cppentry.com)(6.0和7.0)以及Watcom就立即出局了,因为它们都不支持局部特化。实际上的实现针对Borland作了一些迂回处理,另外,在GCC上,如果向它传递一个指向中间临时变量而不是单独的变量的指针,GCC就会给出一个二义性错误。但是在CodeWarrior、Comeau、Digital Mars、Intel以及Visual C++(www.cppentry.com)7.1上,所有情况下工作得都同样出色。

当然,如果可能的话,使用一个统一的方式去处理指针和引用是个更为诱人的想法,但考虑到这个版本的实现的"不彻底性",我们就不难想象为什么Boost中的实现要将其自身限制在只能处理指针的前提下了。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇19.8.1 interface_cast_addref 下一篇19.8.8 interface_cast 尾声

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: