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

Rei*_*ica 5 c++ virtual-inheritance downcast

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • static_cast 明确禁止从虚拟基类进行转换:

    5.2.9/2 ...并且B既不是虚拟基类,D也不是虚基类的基类D....

  • dynamic_cast 也不能这样做,因为downcast需要一个多态类

    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);
  }
};
Run Code Online (Sandbox Code Playgroud)

也就是说,我知道这T_Base是一个基类T_MostDerived(并且我知道它T_MostDerived实际上是派生类型最多),但我对它们一无所知; Bar是我的代码,是未知客户端可以使用的库的一部分.我可以检测到它是一个非多态的虚拟基础,但我无法在这种情况下强制转换它.

Che*_*Alf 4

有一个从MostDerived&到其 的隐式明确转换ViBase&。Astatic_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 );
}
Run Code Online (Sandbox Code Playgroud)

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