I'm a bit stuck with such awful problem of bad class hierarchy.
class A {...}; // abstract
class B : public A {...}; // abstract
class C {...}; // abstract
class a1 : public B, public C {...}; // indirect inheritance of A
class c1 : public A, public C {...};
The question: is it possible convert a pointer(reference) to C into pointer(reference) to class A.
I understand that best solution is to make just class C inherited from A, but still it can cause some problems with class a1 (two base A).
The question: is it possible convert a pointer(reference) to C into pointer(reference) to class A.
Not unless the classes are polymorphic i.e. have at least one virtual member function. In that case dynamic_cast can be used to side cast, as shown in StoryTeller's answer.
However, if that C pointer(reference) points to a child that inherits A, then you can first cast down to that child pointer, and then to A.
c1 c;
C& cref = c;
A& aref = static_cast<c1&>(cref);
That's of course not necessarily ideal because you cannot convert just any arbitrary C pointer whose concrete type is unknown.
I understand that best solution is to make just class C inherited from A
If you did this, then all C pointers would be implicitly convertible to A pointers.
but still it can cause some problems with class a1 (two base A).
To work around the problems, you need shared bases i.e virtual inheritance.
struct A {}; // abstract
struct B : virtual A {}; // abstract
struct C : virtual A {}; // abstract
struct a1 : B, C {}; // indirect inheritance of A
struct c1 : C {};
int main() {
c1 c;
C& cref = c;
A& aref = cref;
}
What you are trying to do is called a "side-cast". And the built-in dynamic_cast expression can do that. But it isn't cheap, and you better not have turned off support for RTTI in your project (you already mentioned your classes are abstract, so that entails virtual functions, whose declarations are needed for RTTI to be generated along).
See it Live
#include <cassert>
struct A {
virtual ~A() = default;
};
struct B : public A {
virtual ~B() = default;
};
struct C {
virtual ~C() = default;
};
struct a1 : public B, public C {
a1() = default;
virtual ~a1() = default;
}; // indirect inheritance of A
int main() {
a1 a;
C* c = &a;
assert(dynamic_cast<A*>(c) != nullptr);
return 0;
}
But your sentiments about needing to re-design your classes are in my opinion highly warranted. The need to do a side cast should not arise.
Both answers above are correct, but the first one is more widely explained, I think.
Another solution that I will possibly use is creating a weird visitor, like:
PointerCreator
{
void visit ( const a1 & _a )
{
m_pointerToA = &_a;
}
void visit ( const c1 & _c )
{
m_pointerToA = &_a;
}
A * m_pointerToA;
};
PointerCreator pC;
a1.accept( pC );
A& = *pC.m_pointerToA;
Related
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
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.
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.
I know that it is not possible to have an instance of an abstract class as a base member of another class, i.e.,
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base & b): b_(b) {};
virtual ~D() {};
private:
Base b_;
}
int main() {
C1 c;
D d(c);
}
will fail to compile with the error
test.cpp:22:10: error: cannot declare field ‘D::b_’ to be of abstract type ‘Base’
The obvious workaround is to use (shared) pointers instead. This, however, makes main somewhat more complicated to read,
int main() {
auto c = std::make_shared<C1>();
D d(c);
}
which I would really like to avoid.
Is there a way to keep the main file as simple as in the above example and still achieve the desired functionality?
You can't. When you are creating D it allocates (in heap or in stack) memory for B. And C1 class needs size of base class B plus size of extra variables/etc in C1 itself even if there are nothing new.
So, use pointers instead.
The error caused by virtual int yield() = 0;. If you use virtual int yield(), it will works. When you used virtual int yield() = 0;, it said that the function is a pure virtual function, so you must override it. So you should give its inheritance class and use the instance of inheritance class in class C1. In a world, virtual int yield() = 0; only remind you that it is only a interface, you must override it. I hope this can help you.
Since Base is an abstract class (has at least one pure virtual function), it can't be instantiated directly.
When you declare D's class member as "Base b_", you are effectively trying to create an instance. You can instead use a pointer there (or some kind of safe/smart pointer).
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base * b): b_(b) {};
virtual ~D() {};
private:
Base *b_; // Use a pointer or safe ptr or something of that sort.
}
int main() {
C1 c;
D d(&c);
}
No. One of the properties of an abstract class is that it cannot be instantiated. That means an instance of an abstract class cannot be a member of another class.
Even if Base was not abstract, your class D's constructor would be slicing the object passed. If passed an instance of C1, the copying (in the initialiser list of D's constructor) would not magically cause an instance of D to contain an object of type C. It would instead create a copy only of the Base part of that object.
In short, your design is broken, and will not work even if - syntactically - it would be possible to simplify the code in main().
I'm learning now the different situations of ambiguity in the virtual derivation on C++.
But I have an error of Ambiguity in my code and I don't understand it's reason...
Here's my Code :
class V {
public:
int v ;
};
class A {
public:
int a ;
};
class B : public A, public virtual V {
};
class C : public A, public virtual V {
};
class D : public B, public C {
public:
void f() ;
};
void g() {
D d ;
B* pb = &d ; // No Problem
A* pa = &d ; // Error: 'A' is ambiguous base of 'D'
V* pv = &d ; // No Problem
}
I don't understand why do I have this error however I don't have errors for the other affectations.
Thank you :-)
This is completely expected in cases of multiple inheritance. What we have here is a case of Diamond inheritance. D now has two copies of A, one inherited from B, and one inherited from C. You need to specify which of B or C the members of A exposed to D come from.
See: Using C++, how do I correctly inherit from the same base class twice?
You might consider:
Using virtual inheritance:
class B : public virtual A, public virtual V {...};
class C : public virtual A, public virtual V {...};
Using composition as a way out of multiple inheritance.
I suggest reading Solving the Diamond Problem with Virtual Inheritance
Let's say we have following code:
struct A{
virtual ~A(){}
void f(){
p = 42;
}
int p;
};
struct B : public virtual A{};
struct C : public virtual A{};
struct D : public B, public C, public A{}; //let's add non-virtual inheritance
int main(){
D* pA = new D();
pA->A::f(); //!
return 0;
}
Is there any way to set p to 42 in the most base class A?
Following construction pA->A::f(); sets p to 42 for non-virtual inherited class A. Can we do that without cast?
First off, there is no cast: you just qualify which version of A you want as there are more than one. Of course, the notation you have chosen actually doesn't work because it doesn't resolve the ambiguity in the first place. I guess you meant to use something like
pA->B::f();
If you don't want to put the burden of choosing which member function to call on the user of your class, you'll have to provide suitable forwarding functions for D e.g.:
void D::f() { this->B::f(); }
I have a quite complex class hierarchy in which the classes are cross-like depending on each other: There are two abstract classes A and C containing a method that returns an instance of C and A, respectively. In their inherited classes I want to use a co-variant type, which is in this case a problem since I don't know a way to forward-declare the inheritance relation ship.
I obtain a "test.cpp:22: error: invalid covariant return type for ‘virtual D* B::outC()’"-error since the compiler does not know that D is a subclass of C.
class C;
class A {
public:
virtual C* outC() = 0;
};
class C {
public:
virtual A* outA() = 0;
};
class D;
class B : public A {
public:
D* outC();
};
class D : public C {
public:
B* outA();
};
D* B::outC() {
return new D();
}
B* D::outA() {
return new B();
}
If I change the return type of B::outC() to C* the example compiles. Is there any way to keep B* and D* as return types in the inherited classes (it would be intuitive to me that there is a way)?
I know of no way of having directly coupled covariant members in C++. You'll have either to add a layer, or implement covariant return yourself.
For the first option
class C;
class A {
public:
virtual C* outC() = 0;
};
class C {
public:
virtual A* outA() = 0;
};
class BI : public A {
public:
};
class D : public C {
public:
BI* outA();
};
class B: public BI {
public:
D* outC();
};
D* B::outC() {
return new D();
}
BI* D::outA() {
return new B();
}
and for the second
class C;
class A {
public:
C* outC() { return do_outC(); }
virtual C* do_outC() = 0;
};
class C {
public:
virtual A* outA() = 0;
};
class D;
class B : public A {
public:
D* outC();
virtual C* do_outC();
};
class D : public C {
public:
B* outA();
};
D* B::outC() {
return static_cast<D*>(do_outC());
}
C* B::do_outC() {
return new D();
}
B* D::outA() {
return new B();
}
Note that this second option is what is done implicitly by the compiler (with some static checks that the static_cast is valid).
As far as I know, there's no way to do this without explicit casting. The problem is that the definition of class B can't know that D is a subclass of C until it sees a full definition of class D, but the definition of class D can't know that B is a subclass of A until it sees a full definition of class B, and so you have a circular dependency. This can't be resolved with forward-declarations because a forward declaration unfortunately cannot specify an inheritance relationship.
There's a similar problem with trying to implement a covariant clone() method using templates, which I found can be solved, but the analogous solution still fails here because the circular reference remains impossible to resolve.
You can't do this due to client side expectation. When using a C instance, you can't tell which kind of C it is (a D or something else). Thus, if you store the B pointer (resulting from a call to the derived class but you don't know it at compile time) into a A pointer, I'm not sure that all the memory stuff will be right.
When you call a method on a polymorphic type, the runtime environment has to check the dynamic type of the object and it moves pointers to suit to your class hierarchy. I'm not sure that you should rely on covariance. Have a look at this