2
votes

From https://en.cppreference.com/w/cpp/language/dynamic_cast:

dynamic_cast < new_type > ( expression )

3) If new_type is a pointer or reference to Base, and the type of expression is a pointer or reference to Derived, where Base is a unique, accessible base class of Derived, the result is a pointer or reference to the Base class subobject within the Derived object pointed or identified by expression. (Note: an implicit conversion and static_cast can perform this conversion as well.)

Sample code:

#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;
}

No error or warning under VC++

warning: direct base 'A' inaccessible in 'D' due to ambiguity under gcc, link

Shouldn't I expect a compile error?


Now I find that if I try to convert D* to A*, a error would occur, but as mentioned above, from D* to B*, no error.

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

link

2
Could you amend this please, you don't have a polymorphic structure here.Bathsheba
@Bathsheba But this casting rule 3) doesn't mention polymorphic type, I aim to make it non-polymorphic here to do some experiments.Rick
Is not B in Your example unique base class of D? No wonder it compilesbartop
In your first case, there is no ambiguity of B within D, so there is no reason to expect an error from dynamic_cast<B*>(pd). VC++ is being kind to you, by warning that A is an ambiguous base of D, but since there is no attempt to convert a D * to an A*, there is no diagnosable error. In the second case, dynamic_cast<A*>(pd), there is a diagnosable error, since the conversion is ambiguous. In short: both compilers are correct, but VC++ gives an additional warning. Compilers are not required to give warnings.Peter
@Rick, the cppreference wording could be improved slightly, but " where Base is a unique, accessible base class of Derived" does not mean "where Derived only has one base class, which is of type Base". It means "there is exactly one base class of type Base among Derived's (possibly multiple) base classes".Jonathan Wakely

2 Answers

3
votes

In that context, unique means that Derived contains new_type only once, not that Derived derives from a single base class.

So, in your example, B is unique, because D contains it only once.

In your example, D contains A twice (once directly, and once through B), so a cast to A cannot be made, as A is not unique.

Note, that "containment" what it counts. So, in this example, C derives from Base twice, yet it is fine, as Base is inherited with the keyword virtual:

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

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

(If I haven't used virtual, then Base would have been ambiguous)

Note: I'd use static_cast instead, as it can do the cast in this case as well. Using dynamic_cast is a little bit misleading here, as the cast will be done compile-time, and not run-time.

0
votes

The warning causes by indirect base class issue in multiple-inheritance model. D has two copies of base class A, one directly and one indirectly via B. When a base class is specified as a virtual base (same as @geza example), there is only one copy of base class' data members shared between virtual base classes.

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;
}

It's well explained here: Multiple Base Classes.