【问题标题】:Is it possible to dynamic_cast between two unrelated types, if they share a common (possibly unknown) child class?如果它们共享一个共同的(可能未知的)子类,是否可以在两个不相关的类型之间进行动态转换?
【发布时间】:2021-04-14 18:58:24
【问题描述】:

对不起,如果问题标题没有意义,但我不确定如何简洁地描述我要解决的问题。问题来了:

  1. 我正在使用一个 C++ 库,它大量使用了一个我们称之为 Base 的类
  2. 这个库有几个不同的子类继承自Base。我们将这些类称为 Child1Child2、.. 等。
  3. 此库允许用户创建自己的Base 子类,并让库使用这些类的实例。我目前有这样的事情:
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 类的指针,没问题,一切都很好。有时我可能需要从库中接收一个指向 Base 对象的指针并用它做一些事情。看起来像这样:

// 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() 方法。但是......我现在需要拥有不止一个可能的Custom 类。具体来说,我需要创建适当的“子”类以从我的Custom 类中的模板参数继承。我的代码现在看起来像这样:

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&lt;&gt; 类没有问题,因为只要模板参数Child_t 实际上是从Base 继承的库的子类之一,我的Custom&lt;&gt; 类可以简单地转换为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&lt;&gt; 类,我的函数 doSomething() 都将具有相同的行为。我最初的想法是将我的 doSomething() 函数移动到接口类。

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

然后让每个Custom&lt;&gt; 类像这样实现接口:

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* 转换为@987654354 @。在这一点上,我被卡住了,不知道该怎么做。我需要在从库中获得的任何Base* 上调用我的doSomething() 函数,但我不知道我实际获得的是哪个自定义子类。我目前看到的唯一解决方案是将dynamic_cast 穷尽所有可能的子类。

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&lt;Child3&gt;Custom&lt;Child4&gt;Custom&lt;Child5&gt; 等类,他们必须记住返回并更新 if-else 链详尽地检查每种可能的情况。所以我的问题是:

  1. 是否有可能以某种方式调用Base* 对象上的doSomething() 函数,而实际上不知道我在编译时有哪个Custom&lt;&gt; 类,并且不简单地尝试所有可能的dynamic_cast?因此我的问题的标题是:我可以以某种方式将Base* 转换为Interface*,因为我知道他们共享一个共同的子类(我只是不知道哪个子类)。
  2. 我是否以完全错误的方式处理这件事?

【问题讨论】:

  • dynamic_cast&lt;Interface*&gt;(base)?
  • ...或者如果只有一个可能的Interface,我会考虑有机会将各种virtual void doSomething() = 0; 直接放在Base 中,避免多重继承和动态转换。将Base 向下转换为Custom 以调用专用方法的初始方法听起来很可疑,我将审查导致该方法的设计决策
  • @MatG 是的,理想情况下接口应该在Base。但是Base 是一个来自我正在链接的库的类,我没有完整的源代码(只有 .h 文件和 .lib 文件)。所以我不能修改Base 来包含doSomething() 函数。

标签: c++ templates polymorphism dynamic-cast static-cast


【解决方案1】:

你应该使用dynamic_cast&lt;Interface*&gt;(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

【讨论】:

  • 非常感谢!出于某种原因,我只是假设 dynamic_cast 不能执行这种类型的转换。我想我的印象是 dynamic_cast 和 static_cast 对可以转换的类型有完全相同的限制。现在我知道了!我应该从一开始就尝试 dynamic_cast。
  • @ChristianDaley 没问题 :) 你也提供了很好的问题和细节 :)
猜你喜欢
  • 1970-01-01
  • 2011-02-08
  • 2012-08-17
  • 1970-01-01
  • 2016-03-10
  • 2020-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多