【问题标题】:Why is dynamic_cast to a non-unique base class type allowed?为什么允许将 dynamic_cast 转换为非唯一基类类型?
【发布时间】:2018-09-28 07:03:45
【问题描述】:

来自https://en.cppreference.com/w/cpp/language/dynamic_cast

dynamic_cast < new_type > ( expression )

3) 如果new_type 是指向Base 的指针或引用,而expression 的类型是指向Derived 的指针或引用,其中Base 是唯一,可访问的Derived 基类,结果是指向或由expression 标识的派生对象中的基类子对象的指针或引用。 (注意:隐式转换和static_cast 也可以执行此转换。)

示例代码:

#include <iostream>
using namespace std;
class A {
//public:
//  virtual ~A() {
//
//  }
};

class B : public A {

};

class C : public B {

};

class D : public B, public A {

};

int main()
{
    D* pd = new D;
    if (B* pa = dynamic_cast<B*>(pd)) {
        cout << "1";
    }
    return 0;
}

VC++下无错误或警告

gcc下warning: direct base 'A' inaccessible in 'D' due to ambiguitylink

我不应该期待一个编译错误吗?


现在我发现如果我尝试将D* 转换为A* 会出现错误,但如上所述,从D* 转换为B*,没有错误。

int main()
{
    D* pd = new D;
    if (A* pa = dynamic_cast<A*>(pd)) {
        cout << "1";
    }
    return 0;
}

link

【问题讨论】:

  • 请您修改一下,这里没有多态结构。
  • @Bathsheba 但是这个强制转换规则 3) 并没有提到多态类型,我的目标是让它成为非多态的,在这里做一些实验。
  • 在您的示例中,B 不是 D 的唯一基类吗?难怪它编译
  • 在您的第一种情况下,BD 中没有歧义,因此没有理由预期dynamic_cast&lt;B*&gt;(pd) 会出现错误。 VC++ 对你很好,警告AD 的模棱两可的基数,但由于没有尝试将D * 转换为A*,因此没有可诊断的错误。在第二种情况下,dynamic_cast&lt;A*&gt;(pd),有一个可诊断的错误,因为转换是不明确的。简而言之:两个编译器都是正确的,但是 VC++ 给出了一个额外的警告。编译器不需要给出警告。
  • @Rick,cppreference 的措辞可以稍微改进,但是“其中 Base 是 Derived 的唯一、可访问的基类”并不意味着“Derived 只有一个基类,它是 Base 类型”。这意味着“在 Derived 的(可能有多个)基类中只有一个 Base 类型的基类”。

标签: c++


【解决方案1】:

在这种情况下,unique 意味着 Derived 包含 new_type 仅一次,而不是 Derived 从单个基类派生。

因此,在您的示例中,B 是唯一的,因为 D 包含它仅一次。

在您的示例中,D 包含 A 两次(一次直接,一次通过B),因此无法转换为A,因为A 是不是唯一的。

注意,“遏制”才是最重要的。所以,在这个例子中,C 两次从Base 派生,但没关系,因为Base 是用关键字virtual 继承的:

struct Base { };
struct A: virtual Base { };
struct B: virtual Base { };
struct C: A, B { };

int main() {
    C c;
    dynamic_cast<Base &>(c);
}

(如果我没有使用virtual,那么Base 会很模糊)

注意:我会改用static_cast,因为在这种情况下它也可以进行强制转换。在这里使用 dynamic_cast 有点误导,因为转换将在编译时完成,而不是在运行时完成。

【讨论】:

  • 是的,他使用了dynamic_cast,而没有任何动态:)
【解决方案2】:

多继承模型中间接基类问题导致的警告。 D 有两份基类A,一份直接通过B 间接复制。当基类被指定为虚拟基类时(与@geza 示例相同),只有一个基类数据成员的副本在虚拟基类之间共享。

class A {
public:
    A() {}
};

class B : virtual public A {
public:
    B() : A() {}
};

class C : public B, virtual public A {
public:
    C() : B() {}
};

int main()
{
  A* pa = static_cast<A *>(new C()); // no more warning since A is virtual
  return 0;
}

这里解释得很好:Multiple Base Classes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-09
    • 2013-07-07
    • 1970-01-01
    • 2020-08-21
    • 2020-05-08
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多