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
withinfoo()
, since the actual type ofpa
can vary with each of
foo()
's invocations. Rather, the compiler must transform the code
doing the access so that the resolution ofX::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 b
but 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 aB
that is actually a base class subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. 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 classB
ofX
, the construction ofX
and the construction of all of its direct or indirect bases that directly or indirectly derive fromB
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 objectobj
, the construction ofobj
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_cast
ing 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
What Is the Size of a Pointer? What Exactly Does It Depend On
Cross-Platform Equivalent to Windows Events
Reading and Writing to Usb (Hid) Interrupt Endpoints on MAC
This Declaration Has No Storage Class or Type Specifier in C++
C++11 Constexpr Flatten List of Std::Array into Array
Dropping Privileges in C++ on Windows
How to Build Libcxx and Libcxxabi by Clang on Centos 7
How to Check If Given Int Exists in Array
How to Speed Up Floating-Point to Integer Number Conversion
Quick and Dirty Way to Profile Your Code
Is Rebasing Dlls (Or Providing an Appropriate Default Load Address) Worth the Trouble
Qtcreator: No Valid Kits Found
Does C++11, 14, 17 or 20 Introduce a Standard Constant for Pi
Why Don't I Need to Specify "Typename" Before a Dependent Type in C++20
Why Does Initializing an Extern Variable Inside a Function Give an Error
Warning C4003 and Errors C2589 and C2059 On: X = Std::Numeric_Limits<Int>::Max();