如果两个不相关的类型共享一个公共(可能未知)child class,是否可以 dynamic_cast?

Is it possible to dynamic_cast between two unrelated types, if they share a common (possibly unknown) child class?

抱歉,如果问题标题没有意义,但我不确定如何简洁地描述我要解决的问题。这是问题所在:

  1. 我正在使用大量使用 class 的 C++ 库,我们称之为 Base
  2. 这个库有几个不同的 child class 继承自 Base。我们将这些 class 称为 Child1Child2、.. 等等
  3. 此库允许用户创建他们自己的 child classes of Base 并让库使用这些 classes 的实例。我目前有这样的东西:
class Custom : public Child1 // inherit from Child1, which inherits from Base
{
public:
  // override virtual functions here
  // ...
  void doSomething(); // Utility function I created
}

然后我使用的库将具有如下功能:

void foo(Base* base);

我可以传递一个指针到我的 Custom class 没问题,一切都很好。有时我可能需要从库中接收指向 Base object 的指针并用它做一些事情。看起来像这样:

// code...
Base *base = getSomeBase(); // getSomeBase() is a function from the library that returns a Base*
Custom* myCustom = static_cast<Custom*>(base); // I always make the library use my `Custom` class, so this is safe.
myCustom->doSomething();

这也没有问题。我可以通过执行 static_cast 来调用我的自定义 doSomething() 方法。然而...我现在需要有不止一种可能Customclass。具体来说,我需要制作适当的“child”class 以从我的 Custom class 中的模板参数继承。我的代码现在看起来像这样:

template <class Child_t>
class Custom : public Child_t // inherit from Child_t, which inherits from Base
{
public:
  // override virtual functions here
  // ...
  void doSomething(); // Utility function I created
}

使库使用我的新模板 Custom<> class 没有问题,因为只要模板参数 Child_t 实际上是库的 child class继承自 Base 的 es,我的 Custom<> class 可以简单地转换为 Base*。尝试朝另一个方向前进时出现问题:

Base *base = getSomeBase();
/* ?????
  Would like to call base->doSomething();
  But I have no idea which Custom class I have received here. "base" could be
  a Child1*, Child2*, etc. There's no way for me to perform a cast.
*/

我卡住了。请注意,无论我从库中收到哪个 Custom<> class,我的函数 doSomething() 都将具有相同的行为。我最初的想法是将我的 doSomething() 函数移动到接口 class.

class Interface
{
public:
  virtual void doSomething() = 0;
}

然后让每个 Custom<> class 像这样实现接口:

template <class Child_t>
class Custom : public Child_t, public Interface
{

  void doSomething() override;
}

这最终无济于事,因为编译器不允许我执行以下操作:

Base *base = getSomeBase();
Interface* interface = static_cast<Interface*>(base); // Error: can't static_cast between unrelated types.
interface->doSomething();

编译器说 InterfaceBase 是不相关的类型。我知道一个事实,我收到的任何 Base* 实际上是一个 Interface*,但编译器不知道这一点,而且我猜,无法执行正确的指针调整来转换 Base*Interface*。在这一点上,我被困住了,不知道该怎么做。我需要在从图书馆获得的任何 Base* 上调用我的 doSomething() 函数,但我不知道我实际获得的是哪个自定义 child class。我目前看到的唯一解决方案是穷举 dynamic_cast 到所有可能的 child class.

Base *base = getSomeBase(); // getSomeBase()
if (auto* c1 = dynamic_cast<Custom<Child1>*>(base))
{
  c1->doSomething();
}
else if (auto* c2 = dynamic_cast<Custom<Child2>*>(base))
{
  c2->doSomething();
}

这是一个丑陋的解决方案。它还会给开发人员带来额外的认知负担,因为如果他们在任何时候决定需要使用 Custom<Child3>Custom<Child4>Custom<Child5> 等 class,他们必须记住返回并更新 if-else 链以详尽检查每种可能的情况。所以我的问题是:

  1. 是否有可能以某种方式在 Base* object 上调用我的 doSomething() 函数,而实际上不知道我在编译时有哪个 Custom<> class,而不是简单地尝试所有可能的 dynamic_cast?因此我的问题的标题是:我能以某种方式将 Base* 转换为 Interface*,因为我知道它们共享一个共同的 child class(我只是不知道哪个 child class).
  2. 我是不是用完全错误的方式来解决这个问题?

你应该使用 dynamic_cast<Interface*>(base)


struct B{virtual ~B(){}};
struct I{virtual int foo()=0;};

struct X:B{};
struct Y:I,X{virtual int foo(){return 10;}};
struct Z:I,X{virtual int foo(){return 20;}};

int main(){
    B* x = new Z;
    I* i = dynamic_cast<I*>(x);
    return i->foo();
}

http://coliru.stacked-crooked.com/a/f7a5787cb9fe80be