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

时间:2014-07-23 20:21:20

标签: c++ downcast virtual-inheritance

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

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的虚基类,也不是{{1}的虚基类的基类}。 ...

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

      

    5.2.7 / 6 否则,dynamic_cast应为多态类型的指针或glvalue(10.3)。

         

    10.3 / 1 ...声明或继承虚函数的类称为多态类。

  • v根本不适用于此。

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

(注意所有引用均来自C ++ 11草案N3485)


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

dynamic_cast

也就是说,我知道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_Base的基类(我知道T_MostDerived实际上是派生类型最多),但我什么都不知道关于他们; T_MostDerived是我的代码,是未知客户端可以使用的库的一部分。我可以检测它是非多态的虚拟基础,但在这种情况下我不能投射

2 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:2)

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

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有一个适当的派生子项,这通常不是这种情况。