C++ Cannot Convert from Base a to Derived Type B via Virtual Base A

C++ cannot convert from base A to derived type B via virtual base A

In order to understand the cast system, you need to dive into the object model.

The classic representation of a simple hierarchy model is containment: if B derives from A then the B object will, in fact, contain an A subobject alongside its own attributes.

With this model downcasting is a simple pointer manipulation by an offset known at compilation time, which depends on the memory layout of B.

This is what static_cast does: a static cast is dubbed static because the computation of what is necessary for the cast is done at compile-time, be it pointer arithmetic or conversions (*).

However, when virtual inheritance kicks in, things tend to become a bit more difficult. The main issue is that with virtual inheritance all subclasses share the same instance of the subobject. In order to do that, B will have a pointer to an A, instead of an A proper, and the A base class object will be instantiated outside of B.

Therefore, it's impossible at compilation time to be able to deduce the necessary pointer arithmetic: it depends on the runtime type of the object.

Whenever there is a runtime type dependency, you need RTTI (RunTime Type Information), and making use of RTTI for casts is the job of dynamic_cast.

In summary:

  • compile-time downcast: static_cast
  • run-time downcast: dynamic_cast

The other two are also compile-time casts, but they are so specific that it's easy to remember what they are for... and they are smelly, so better not use them at all anyway.

(*) As noted by @curiousguy in the comments, this only holds for downcasting. A static_cast allows upcasting regardless of virtual or simple inheritance, though then the cast is also unnecessary.

Downcasting in C++ with virtual base class

Is this simply impossible to do?

Yes.

How does the C++ standard (11 or 14) state that this is impossible?

It is stated under the requirements of static_cast.

[expr.static.cast] - emphasis mine

11 A prvalue of type “pointer to cv1 B,” where B is a class type,
can be converted to a prvalue of type “pointer to cv2 D,” where D is a
class derived (Clause [class.derived]) from B, if a valid standard
conversion from “pointer to D” to “pointer to B” exists ([conv.ptr]),
cv2 is the same cv-qualification as, or greater cv-qualification than,
cv1, and B is neither a virtual base class of D nor a base class of
a virtual base class of D
.

As you can see, Base cannot be virtual or even a non-virtual base of another virtual base.

What is the underlying implementation of a standard compiler like gcc which makes this feature impossible?

Virtual inheritance is a mechanism that allows the same Base subobject to be shared between different intermediate base classes. For instance, if you were to add:

class Derived2: public virtual Base {};
class MostDerived: public Derived, public Derived2 {};

Then MostDerived will have a sub-object of type Derived and type Derived2, but unlike regular inheritance, they won't each have their own Base sub-object (thus 2 Base sub-objects in MostDerived). Instead there will be only one Base sub-object, and both Derived and Derived2 will refer to it.

This is commonly accomplished by Derived and Derived2 accessing the Base indirectly through some pointer. They cannot rely on it being placed inside themselves at a certain offset. The location of their Base is determined by the most derived object. So if you have a Base*, there is no one well-determined way to go to the Derived object that refers to it.

In the case of non-virtual inheritance, the Base would be located at a place known to the compiler relative to the Derived sub-object that contains it fully. So the compiler could do the pointer arithmetic required to obtain the Derived sub-object. But in your case, it cannot, because there is no guarantee of relative placement. The access is (generally) indirect.


You didn't ask, but I might as well address that too. The reason a dynamic_cast works for polymorphic classes, is that polymorphic classes can have RTTI associated with them in their vtable (if they have one). And because that Base* will point to the vtable of MostDerived (or Derived), the dynamic_cast mechanism can leverage information written in that table to figure out how to obtain the sub-object it needs. The table can contain all the offset information it needs to get from any one sub-object to any other sub-object. And because it is the table of the most derived object (at run-time), that information is fixed and reliable.

Why is it disallowed to convert from VirtualBase::* to Derived::*?

Lippman's "Inside the C++ Object model" has a discussion about this:

[there] is the need to make the virtual base class location within
each derived class object available at runtime. For example, in the
following program fragment:

class X { public: int i; }; 
class A : public virtual X { public: int j; };
class B : public virtual X { public: double d; };
class C : public A, public B { public: int k; };
// cannot resolve location of pa->X::i at compile-time
void foo( const A* pa ) { pa->i = 1024; }

main() {
foo( new A );
foo( new C );
// ...
}

the compiler cannot fix the physical offset of X::i accessed through
pa within foo(), since the actual type of pa can vary with each of
foo()'s invocations. Rather, the compiler must transform the code
doing the access so that the resolution of X::i can be delayed until
runtime.

Essentially, the presence of a virtual base class invalidates bitwise copy
semantics
.

Why doesn't this code cast a base class into a derived class in c++?

Whenever you push an object of b into vector vec of Base Objects, you create another object from temp which is purely of type base. You might be thinking (and you are not right!) that element which is being stored in vector will be of type Base but it will be holding an object of type bbut it's not how you achieve Dynamic Polymorphism in C++.

The statements:

std::vector<Base> vec; // 1
b temp; // 2
vec.push_back(temp); // 3

The third line will create a different object to type Base by calling assignment operator of base class Base operator=(const Base& ).

Also,b* temp = (b*)&vec[i]; is an undefined behavior because you are explicitly trying to cast a pointer to object of base to it's derived class type b but it doesn't hold object of type b and hence, you may get unexpected behavior.

NOTE:

Use dynamic_cast for casting between base and derived class as it will make sure that the conversion is valid. Otherwise, it will return nullptr. However, you need to make your base class polymorphic by having at least 1 virtual function.

If the cast is successful, dynamic_cast returns a value of type new-type. If the cast fails and new-type is a pointer type, it returns a null pointer of that type. If the cast fails and new-type is a reference type, it throws an exception that matches a handler of type std::bad_cast.

SOLUTION:

Use vector of pointers to base class to achieve run-time polymorphism.

std::vector<base *> vec;
for (int i = 0; i < 5; i++) {
b *temp = new b();
vec.push_back(temp);
}

for (int i = 0; i < 5; i++) {
b* temp = dynamic_cast<b *>(vec[i]); // For dynamic cast, you need to add virtual functions to your base class
if (temp != nullptr)
std::cout << temp->d << std::endl;
}

EDIT:

Object Slicing is also a solution for your problem. Object Slicing is the right keyword for this type of problems. Here is the definition of the Object Slicing

Object slicing happens when a derived class object is assigned to a base class object, additional attributes of a derived class object are sliced off to form the base class object.

I am quoting one of the answer in the link below. See this answer and answer for the best explanation and possible solution with some code snippet. See this article, it has explained the problem when pushing object of Derived class in a vector of base class.

"Slicing" is where you assign an object of a derived class to an instance of a base class, thereby losing part of the information - some of it is "sliced" away.

For example,

class A {
int foo;
};

class B : public A {
int bar;
};

So an object of type B has two data members, foo and bar.

Then if you were to write this:

B b;
A a = b;

Then the information in b about member bar is lost in a.

Can we convert a base class pointer to a derived class pointer without knowing its real type?

The thing is: You need to know the type of each variable at compile time. Hence you cannot have a magical function f that takes an A* a and returns either B* or C* based on the runtime information of the actual type of the object pointed to by a. Even for the the following example the function B::f will be called but the return type will be of type A* since this is determined at compile time.

#include <iostream>
struct A {
virtual A* get_ptr() { std::cout << "A!\n"; return this; }
virtual ~A() {}
};

struct B: A {
B* get_ptr() { std::cout << "B!\n"; return this; }
};

void f(A*) { std::cout << "But here A!\n";}

void f(B*) { std::cout << "But here B!\n";}

int main() {
B b;
A* a = &b;
f(a->get_ptr());
}

Will print B! (since it is virtual) and then call the A* (since the compiler assumes that a->get_ptr() returns a A*) overload.

What you can do is:

  • Switch on an enum that you store in the object that corresponds to its type.
  • Trial and error with dynamic_cast.

Addendum: You will probably not need to cast a pointer to the object you have to the actual type of the object, since you can (as done above) just call the virtual functions and those will call the functions belonging to the actual type of your object. Hence, when you think that you need to do the cast as you described, think again, you probably will not need it.

If you still think you need it, reconsider your design decisions, you are probably doing something very weird.

Can you static_cast this to a derived class in a base class constructor then use the result later?

In my opinion this is well-defined according to the current wording of the standard: the C object exists at the time of the static_cast, although it is under construction and its lifetime has not yet begun. This would seem to make the static_cast well-defined according to [expr.static.cast]/11, which reads in part:

... If the prvalue of type “pointer to cv1 B” points to a B that is actually a base class subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined.

It doesn't say that the D object's lifetime must have begun.

We might also want to look at the explicit rule about when it becomes legal to perform an implicit conversion from derived to base, [class.cdtor]/3:

To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior. To form a pointer to (or access the value of) a direct non-static member of an object obj, the construction of obj shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.

According to this rule, as soon as the compiler starts constructing the base class A<C>, it is well-defined to implicitly convert from C* to A<C>*. Before that point, it results in UB. The reason for this, basically, has to do with virtual base classes: if the path by which A<C> is inherited by C contains any virtual inheritance, the conversion may rely on data that are set up by one of the constructors in the chain. For a conversion from base to derived, if there is indeed any virtual inheritance on the chain, static_cast will not compile, so we don't really need to ask ourselves the question, but are those data sufficient for going the other way?

I really can't see anything in the text of the standard, nor any rationale, for the static_cast in your example not being well-defined, nor in any other case of static_casting from base to derived when the reverse implicit conversion (or static_cast) would be allowed (excepting the case of virtual inheritance, which as I said before, leads to a compile error anyway).

(Would it be well-defined to do it even earlier? In most cases this won't be possible; how could you possibly attempt to static_cast from B* to D* before the conversion from D* to B* is allowed, without having obtained the B* pointer precisely by doing the latter? If the answer is that you got from D* to B* through an intermediate base class C1 whose constructor has started, but there is another intermediate base class C2 sharing the same B base class subobject and its construction hasn't started yet, then B is a virtual base class, and again, this means the compiler will stop you from then trying to static_cast from B* back down to D*. So I think there are no issues left to resolve here.)

Cannot convert from pointer to base class to pointer to derived class

The problem comes from connection below. As dynamic_cast is working slower the automatically generated qt_static_metacall function used static_cast which is not able to cast in case of multiple inheritance

ElecClock::ElecClock(QWidget *parent) :
Clock(parent)
{
......
---> connect(timer, SIGNAL(timeout()), this, SLOT(showTime()));
......
}

As solution I just can advice to encapsulate connection into function and call the function only if dynamic and static types of object are same. Otherwise you have to remove that line or refuse from multiple inheritance.

C2440 static_cast cannot convert from base class to derived class

These two casts have differing meaning.

The first cast:

const GPSMessage* message = static_cast<const GPSMessage*>(&msg); // Compiles fine

This means that msg is actually a GPSMessage (or a derived) object. You ask the compiler to threat msg as a GPSMessage. No new object will be created, message will point to msg. If msg is not actually a GPSMessage (or derived), then this cast has Undefined Behavior.

Btw, the following cast have the same meaning (casting to a reference):

const GPSMessage& message = static_cast<const GPSMessage&>(msg); // same meaning, which results in a reference instead of a pointer

About the second cast:

const GPSMessage message1 = static_cast<const GPSMessage>(msg);   // Error C2440

This means, that you create a new object, message1 from msg. You should give a way to make this conversion possible. For example, you should create a constructor for GPSMessage which has a ThreadedMessage parameter:

struct GPSMessage {
GPSMessage(const ThreadedMessage &); // needed constructor
};

Or create a conversion operator for ThreadedMessage to GPSMessage:

struct ThreadedMessage {
operator GPSMessage(); // conversion operator
};

Is converting a reinterpret_cast'd derived class pointer to base class pointer undefined behavior?

static_cast (or an implicit derived-to-base-pointer conversion, which does exactly the same thing) is substantially different from reinterpret_cast. There is no guarantee that that the base subobject starts at the same address as the complete object.

Most implementations place the first base subobject at the same address as the complete object, but of course even such implementations cannot place two different non-empty base subobjects at the same address. (An object with virtual functions is not empty). When the base subobject is not at the same address as the complete object, static_cast is not a no-op, it involves pointer adjustment.

There are implementations that never place even the first base subobject at the same address as the complete object. It is allowed to place the base subobject after all members of derived, for example. IIRC the Sun C++ compiler used to layout classes this way (don't know if it's still doing that). On such an implementation, this code is almost guaranteed to fail.

Similar code with B having more than one base will fail on many implementations. Example.



Related Topics



Leave a reply



Submit