首页 文章

如何从非多态虚拟基类转发?

提问于
浏览
5

当没有涉及虚函数时,有没有办法从虚拟基类转发到派生类?这里有一些代码来演示我在说什么:

struct Base1
{
  int data;
};

struct Base2
{
  char odd_size[9];
};

struct ViBase
{
  double value;
};


struct MostDerived : Base1, Base2, virtual ViBase
{
  bool ok;
};


void foo(ViBase &v)
{
  MostDerived &md = somehow_cast<MostDerived&>(v);  //but HOW?
  md.ok = true;
}


int main()
{
  MostDerived md;
  foo(md);
}

请注意,该代码仅用于演示 . 我的真实场景相当复杂,涉及模板参数和从一个到另一个的转换,只知道第一个是第二个的基础;它可以是普通或虚拟的基础,它可能有也可能没有虚函数 . (参见底部的简化示例) . 我可以使用类型特征检测多态情况和虚拟/非虚拟基本情况,并解决除非多态虚拟基础之外的所有情况 . 这就是我要问的问题 .

我真的想不出一种做演员的方法:

  • 隐含的转换是正确的;这些只做了预告 .

从虚拟基类中明确禁止

  • static_cast

5.2.9 / 2 ...和B既不是D的虚基类,也不是D的虚基类的基类 .

  • dynamic_cast 也不能这样做,因为向下转换需要多态类

5.2.7 / 6否则,v应为多态类型的指针或glvalue(10.3) . 10.3 / 1 ...声明或继承虚函数的类称为多态类 .

  • reinterpret_cast 完全不适用于此处 .

如果 MostDerived 至少有一个虚函数,当然可以用 dynamic_cast 来解决 . 但如果没有,有没有办法进行演员表演?

(注:所有报价均来自C 11草案N3485)


鉴于过多关注上述示例代码的评论,这里是我的真实情况的草图:

template <class T_MostDerived>
struct Bar
{
  template <class T_Base>
  void foo(T_Base &b, typename std::enable_if<std::is_base_of<T_Base, T_MostDerived>::value>::type * = nullptr)
  {
    T_MostDerived &md = somehow_cast<T_MostDerived>(b);
    do_stuff_with(md);
  }
};

也就是说,我知道 T_BaseT_MostDerived 的基类(我知道 T_MostDerived 实际上是派生类型最多的),但我对它们一无所知; Bar 是我的代码,是未知客户端可以使用的库的一部分 . 在这种情况下,我可以检测到它是否会抛出它 .

2 回答

  • 2

    从MostDerived到它的ViBase&有一个隐含的无法转换的转换 . static_cast可以明确表达这样的转换,也可以进行相反的转换 . 这就是static_cast所做的那种转换 .

    由于OP注意到 static_cast 从虚拟基地下来是无效的 .

    下面的源代码说明了原因:

    #include <iostream>
    using namespace std;
    
    struct B { virtual ~B(){} };
    struct D: virtual B {};
    struct E: virtual B {};
    struct X: D, E {};
    
    auto main() -> int
    {
        X   x;
        B&  b = static_cast<E&>( x );
    
        // Can't do the following for the address adjustment that would work for
        // D sub-object won't work for E sub-object, yet declarations of D and E
        // are identical -- so the address adjustment can't be inferred from that.
        //
        //static_cast<D&>( b );
    
        // This is OK:
        dynamic_cast<D&>( b );
    }
    

    基本上,如图所示,您无法单独从 D (或 E )的声明推断地址调整 . 编译器也不能 . 这也排除了 reinterpret_cast .

  • 4

    这需要一个黑客 . 向下转换需要数学,因为多重继承可能会将基类放在派生类中的某个任意位置 . 但是,如果您知道基类是虚拟继承的,那么派生类中应该只有一个实例 . 这意味着您可以创建转换函数:

    struct MostDerived : Base1, Base2, virtual ViBase
    {
      bool ok;
      template <typename T> static MostDerived * somehow_cast (T *v) {
        static MostDerived derived;
        static T &from = derived;
        static size_t delta
          = reinterpret_cast<char *>(&from) - reinterpret_cast<char *>(&derived);
        char *to = reinterpret_cast<char *>(v);
        return reinterpret_cast<MostDerived *>(to - delta);
      }
    };
    

    特殊C演员给你的是这个功能不是类型安全 . 这个函数盲目地假设传入的 ViBase 有一个适当的派生子进入,这通常不是这种情况 .

相关问题