c++ reinterpret_cast, virtual, and templates ok? - c++

In C++, assume following class hierarchy:
class BaseClass { };
class ChildClass : public BaseClass { };
Further assume factory classes for these two classes with a common, templated base class:
template<typename T>
class Factory {
public:
virtual T* create() = 0;
};
class BaseClassFactory : public Factory<BaseClass> {
public:
virtual BaseClass* create() {
return new BaseClass(&m_field);
}
private:
SomeClass m_field;
};
class ChildClassFactory : public Factory<ChildClass> {
public:
virtual ChildClass* create() {
return new ChildClass(&m_field);
}
private:
SomeOtherClass m_field; // Different class than SomeClass
};
Note that the size/internal structure of ChildClassFactory and BaseClassFactory is different due to their different fields.
Now, if a have an instance of ChildClassFactory (or Factory<ChildClass>), can I safely cast it to Factory<BaseClass> (via reinterpret_cast)?
Factory<ChildClass>* childFactory = new ChildClassFactory();
// static_cast doesn't work - need to use reinterpret_cast
Factory<BaseClass>* baseFactory = reinterpret_cast<Factory<BaseClass>*>(childFactory);
// Does this work correctly? (i.e. is "cls" of type "ChildClass"?)
BaseClass* cls = baseFactory->create();
I know that you can't always cast templated classes this way, but in this special case a cast should be safe, shouldn't it?
I've tested it with Visual C++ 2010 and it does work. My question now is whether this is portable to other compilers?
Update: Since there has been some confusion let me clarify some more what's (supposed to be) important in my example:
ChildClass is a child class of BaseClass
A user of Factory<BaseClass> doesn't know what child class of BaseClass will be created. All he knows is that BaseClass is created.
Factory<T> has no fields of its own (other than the vtable).
Factory::create() is virtual

No, it is not. You may not use the result of a reinterpret_cast other than to cast stuff back, except for a few special cases:
ISO14882:2011(e) 5.2.10-7:
An object pointer can be explicitly converted to an object pointer of
a different type.70 When a prvalue v of type “pointer to T1” is
converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout
types (3.9) and the alignment requirements of T2 are no stricter than
those of T1, or if either type is void. Converting a prvalue of type
“pointer to T1” to the type “pointer to T2” (where T1 and T2 are
object types and where the alignment requirements of T2 are no
stricter than those of T1) and back to its original type yields the
original pointer value. The result of any other such pointer
conversion is unspecified.
To make a possible failure scenario more clear, consider multiple inheritance, where using a static_cast or dynamic_cast would sometimes adjust the pointer value, but reinterpret_cast will not. Consider casting in this example from A* to B*:
struct A { int x; };
struct B { int y; };
struct C : A, B { };
To understand how your code fails in a different way too, consider how most compilers implement the virtual function call mechanism: With virtual pointers. Your instance of ChildClassFactory will have a virtual pointer, pointing to the virtual table of ChildClassFactory. Now when you reinterpret_cast this beast, it just happens to "work" incidentally, because the compiler expects some virtual pointer, pointing to a virtual table that will have the same/similar layout. But it will still contain the values pointing to the ChildCLassFactory virtual functions, thus these functions would be called. All of this is long after invoking undefined behaviour. It is as if you are jumping with a car into a large canyon and thinking "hey, everything is driving fine" just because you have not hit the ground yet.

No, reinterpret_cast is only to be used for lowlevel code since it will not perfrom the correct address manipulation. Use static_cast or dynamic_cast instead,
Why do you want two factories this does not fit in the GoF factory pattern.
reinterpret_cast is not the way to do it since it is slow (runtime checks) and is not a nice OO design (you want to use the polymophisme build into the language).
Instead make constructors in the factory class that produces the types you are after and then have these call the constructor of the individual types.
The factory pattern allows you to be by ignorant of changes in your implementaion, which is a good thing since you minimize your dependencies, and allows for easier maintainance in the future of the code.

I've ticked the original answer above (to give him the credit), but I thought I'd sum up what I've learned here.
So, the basic problem is that it's not defined how dispatching virtual calls must be implemented.
This means that the data structure(s) that are internally used for virtual call dispatching (e.g. vtables) may or may not be bit compatible among template instantiations created from the same template.

Related

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

We ran into this scenario in our codebase at my work, and we had a big debate over whether this is valid C++ or not. Here is the simplest code example I could come up with:
template <class T>
class A {
public:
A() { subclass = static_cast<T*>(this); }
virtual void Foo() = 0;
protected:
T* subclass;
};
class C : public A<C> {
public:
C(int i) : i(i) { }
virtual void Foo() { subclass->Bar(); }
void Bar() { std::cout << "i is " << i << std::endl; }
private:
int i;
};
int main() {
C c(5);
c.Foo();
return 0;
}
This code works 100% of the time in practice (as long as the template parameter type matches the subclass type), but if we run it through a runtime analyzer, it tells us that the static_cast is invalid because we're casting this to a C* but the C constructor hasn't run yet. Sure enough, if we change the static_cast to a dynamic_cast, it returns nullptr and this program will fail and crash when accessing i in Bar().
My intuition is that it should always be possible to replace static_cast with dynamic_cast without breaking your code, suggesting that the original code in fact is depending on compiler-specific undefined behavior. However, on cppreference it says:
If the object expression refers or points to is actually a base class subobject of an object of type D, the result refers to the enclosing object of type D.
The question being, is it a base class subobject of an object of type D before the object of type D has finished being constructed? Or is this undefined behavior? My level of C++ rules lawyering is not strong enough to work this out.
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.)

static_cast to access members of another base class returns wrong if own base is empty [duplicate]

Consider the following code:
struct Base {};
struct Derived : public virtual Base {};
void f()
{
Base* b = new Derived;
Derived* d = static_cast<Derived*>(b);
}
This is prohibited by the standard ([n3290: 5.2.9/2]) so the code does not compile, because Derived virtually inherits from Base. Removing the virtual from the inheritance makes the code valid.
What's the technical reason for this rule to exist?
The technical problem is that there's no way to work out from a Base* what the offset is between the start of the Base sub-object and the start of the Derived object.
In your example it appears OK, because there's only one class in sight with a Base base, and so it appears irrelevant that the inheritance is virtual. But the compiler doesn't know whether someone defined another class Derived2 : public virtual Base, public Derived {}, and is casting a Base* pointing at the Base subobject of that. In general[*], the offset between the Base subobject and the Derived subobject within Derived2 might not be the same as the offset between the Base subobject and the complete Derived object of an object whose most-derived type is Derived, precisely because Base is virtually inherited.
So there's no way to know the dynamic type of the complete object, and different offsets between the pointer you've given the cast, and the required result, depending what that dynamic type is. Hence the cast is impossible.
Your Base has no virtual functions and hence no RTTI, so there certainly is no way to tell the type of the complete object. The cast is still banned even if Base does have RTTI (I don't immediately know why), but I guess without checking that a dynamic_cast is possible in that case.
[*] by which I mean, if this example doesn't prove the point then keep adding more virtual inheritance until you find a case where the offsets are different ;-)
static_cast can perform only those casts where memory layout between the classes is known at compile-time. dynamic_cast can check information at run-time, which allows to more accurately check for cast correctness, as well as read run-time information regarding the memory layout.
Virtual inheritance puts a run-time information into each object which specifies what is the memory layout between the Base and Derived. Is one right after another or is there an additional gap? Because static_cast cannot access such information, the compiler will act conservatively and just give a compiler error.
In more detail:
Consider a complex inheritance structure, where - due to multiple inheritance - there are multiple copies of Base. The most typical scenario is a diamond inheritance:
class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
In this scenario Bottom consists of Left and Right, where each has its own copy of Base. The memory structure of all the above classes is known at compile time and static_cast can be used without a problem.
Let us now consider the similar structure but with virtual inheritance of Base:
class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
Using the virtual inheritance ensures that when Bottom is created, it contains only one copy of Base that is shared between object parts Left and Right. The layout of Bottom object can be for example:
Base part
Left part
Right part
Bottom part
Now, consider that you cast Bottom to Right (that is a valid cast). You obtain a Right pointer to an object that is in two pieces: Base and Right have a memory gap in between, containing the (now-irrelevant) Left part. The information about this gap is stored at run-time in a hidden field of Right (typically referred to as vbase_offset). You can read the details for example here.
However, the gap would not exist if you would just create a standalone Right object.
So, if I give you just a pointer to Right you do not know at compile time if it is a standalone object, or a part of something bigger (e.g. Bottom). You need to check the run-time information to properly cast from Right to Base. That is why static_cast will fail and dynamic_cast will not.
Note on dynamic_cast:
While static_cast does not use run-time information about the object, dynamic_cast uses and requires it to exist! Thus, the latter cast can be used only on those classes which contain at least one virtual function (e.g. a virtual destructor)
Fundamentally, there's no real reason, but the intention is that
static_cast be very cheap, involving at most an addition or a
subtraction of a constant to the pointer. And there's no way to
implement the cast you want that cheaply; basically, because the
relative positions of Derived and Base within the object may change
if there is additional inheritance, the conversion would require a good
deal of the overhead of dynamic_cast; the members of the committee
probably thought that this defeats the reasons for using static_cast
instead of dynamic_cast.
Consider the following function foo:
#include <iostream>
struct A
{
int Ax;
};
struct B : virtual A
{
int Bx;
};
struct C : B, virtual A
{
int Cx;
};
void foo( const B& b )
{
const B* pb = &b;
const A* pa = &b;
std::cout << (void*)pb << ", " << (void*)pa << "\n";
const char* ca = reinterpret_cast<const char*>(pa);
const char* cb = reinterpret_cast<const char*>(pb);
std::cout << "diff " << (cb-ca) << "\n";
}
int main(int argc, const char *argv[])
{
C c;
foo(c);
B b;
foo(b);
}
Although not really portable, this function shows us the "offset" of A and B. Since the compiler can be quite liberal in placing the A subobject in case of inheritance (also remember that the most derived object calls the virtual base ctor!), the actual placement depends on the "real" type of the object. But since foo only gets a ref to B, any static_cast (which works at compile time by at most applying some offset) is bound to fail.
ideone.com (http://ideone.com/2qzQu) outputs for this:
0xbfa64ab4, 0xbfa64ac0
diff -12
0xbfa64ac4, 0xbfa64acc
diff -8
static_cast is a compile time construct. it checks for the validity of cast at compile time and gives an compilation error if invalid cast.
virtualism is a runtime phenomenon.
Both can't go together.
C++03 Standard §5.2.9/2 and §5.2.9/9 ar relevant in this case.
An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.
I suppose, this is due to classes with virtual inheritance having different memory layout. The parent has to be shared between children, therefore only one of them could be laid out continuously. That means, you are not guaranteed to be able to separate a continuous area of memory to treat it as a derived object.

Why can't static_cast be used to down-cast when virtual inheritance is involved?

Consider the following code:
struct Base {};
struct Derived : public virtual Base {};
void f()
{
Base* b = new Derived;
Derived* d = static_cast<Derived*>(b);
}
This is prohibited by the standard ([n3290: 5.2.9/2]) so the code does not compile, because Derived virtually inherits from Base. Removing the virtual from the inheritance makes the code valid.
What's the technical reason for this rule to exist?
The technical problem is that there's no way to work out from a Base* what the offset is between the start of the Base sub-object and the start of the Derived object.
In your example it appears OK, because there's only one class in sight with a Base base, and so it appears irrelevant that the inheritance is virtual. But the compiler doesn't know whether someone defined another class Derived2 : public virtual Base, public Derived {}, and is casting a Base* pointing at the Base subobject of that. In general[*], the offset between the Base subobject and the Derived subobject within Derived2 might not be the same as the offset between the Base subobject and the complete Derived object of an object whose most-derived type is Derived, precisely because Base is virtually inherited.
So there's no way to know the dynamic type of the complete object, and different offsets between the pointer you've given the cast, and the required result, depending what that dynamic type is. Hence the cast is impossible.
Your Base has no virtual functions and hence no RTTI, so there certainly is no way to tell the type of the complete object. The cast is still banned even if Base does have RTTI (I don't immediately know why), but I guess without checking that a dynamic_cast is possible in that case.
[*] by which I mean, if this example doesn't prove the point then keep adding more virtual inheritance until you find a case where the offsets are different ;-)
static_cast can perform only those casts where memory layout between the classes is known at compile-time. dynamic_cast can check information at run-time, which allows to more accurately check for cast correctness, as well as read run-time information regarding the memory layout.
Virtual inheritance puts a run-time information into each object which specifies what is the memory layout between the Base and Derived. Is one right after another or is there an additional gap? Because static_cast cannot access such information, the compiler will act conservatively and just give a compiler error.
In more detail:
Consider a complex inheritance structure, where - due to multiple inheritance - there are multiple copies of Base. The most typical scenario is a diamond inheritance:
class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
In this scenario Bottom consists of Left and Right, where each has its own copy of Base. The memory structure of all the above classes is known at compile time and static_cast can be used without a problem.
Let us now consider the similar structure but with virtual inheritance of Base:
class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
Using the virtual inheritance ensures that when Bottom is created, it contains only one copy of Base that is shared between object parts Left and Right. The layout of Bottom object can be for example:
Base part
Left part
Right part
Bottom part
Now, consider that you cast Bottom to Right (that is a valid cast). You obtain a Right pointer to an object that is in two pieces: Base and Right have a memory gap in between, containing the (now-irrelevant) Left part. The information about this gap is stored at run-time in a hidden field of Right (typically referred to as vbase_offset). You can read the details for example here.
However, the gap would not exist if you would just create a standalone Right object.
So, if I give you just a pointer to Right you do not know at compile time if it is a standalone object, or a part of something bigger (e.g. Bottom). You need to check the run-time information to properly cast from Right to Base. That is why static_cast will fail and dynamic_cast will not.
Note on dynamic_cast:
While static_cast does not use run-time information about the object, dynamic_cast uses and requires it to exist! Thus, the latter cast can be used only on those classes which contain at least one virtual function (e.g. a virtual destructor)
Fundamentally, there's no real reason, but the intention is that
static_cast be very cheap, involving at most an addition or a
subtraction of a constant to the pointer. And there's no way to
implement the cast you want that cheaply; basically, because the
relative positions of Derived and Base within the object may change
if there is additional inheritance, the conversion would require a good
deal of the overhead of dynamic_cast; the members of the committee
probably thought that this defeats the reasons for using static_cast
instead of dynamic_cast.
Consider the following function foo:
#include <iostream>
struct A
{
int Ax;
};
struct B : virtual A
{
int Bx;
};
struct C : B, virtual A
{
int Cx;
};
void foo( const B& b )
{
const B* pb = &b;
const A* pa = &b;
std::cout << (void*)pb << ", " << (void*)pa << "\n";
const char* ca = reinterpret_cast<const char*>(pa);
const char* cb = reinterpret_cast<const char*>(pb);
std::cout << "diff " << (cb-ca) << "\n";
}
int main(int argc, const char *argv[])
{
C c;
foo(c);
B b;
foo(b);
}
Although not really portable, this function shows us the "offset" of A and B. Since the compiler can be quite liberal in placing the A subobject in case of inheritance (also remember that the most derived object calls the virtual base ctor!), the actual placement depends on the "real" type of the object. But since foo only gets a ref to B, any static_cast (which works at compile time by at most applying some offset) is bound to fail.
ideone.com (http://ideone.com/2qzQu) outputs for this:
0xbfa64ab4, 0xbfa64ac0
diff -12
0xbfa64ac4, 0xbfa64acc
diff -8
static_cast is a compile time construct. it checks for the validity of cast at compile time and gives an compilation error if invalid cast.
virtualism is a runtime phenomenon.
Both can't go together.
C++03 Standard §5.2.9/2 and §5.2.9/9 ar relevant in this case.
An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.
I suppose, this is due to classes with virtual inheritance having different memory layout. The parent has to be shared between children, therefore only one of them could be laid out continuously. That means, you are not guaranteed to be able to separate a continuous area of memory to treat it as a derived object.

Does dynamic_cast really work for multiple inheritance?

I wanted to see if it's possible to create "interfaces", inherit them, and then check at runtime if any random class implements that interface. This is what I have:
struct GameObject {
int x,y;
std::string name;
virtual void blah() { };
};
struct Airholder {
int oxygen;
int nitrogen;
};
struct Turf : public GameObject, public Airholder {
Turf() : GameObject() {
name = "Turf";
}
void blah() { };
};
void remove_air(GameObject* o) {
Airholder* a = dynamic_cast<Airholder*>(o);
if(!a) return;
a->oxygen = 0;
a->nitrogen = 0;
};
Now, it works. The documentation says that it works, the test example works.. But also, it didn't compile until I added a virtual method to GameObject. The thing is, I really don't know if the feature is intended to be used like that. What made me wonder there is the fact that I have to declare a virtual function for the class I'm checking. But obviously, there is none, the class I'm checking itself has no virtual functions, in fact my whole code has nothing to do with virtual functions, it's an entirely different approach.
So, I guess my question is: If what I'm doing really works, why do I need a virtual function to give my class a vtable? Why can't I declare the class a "runtime type" or something without virtual functions?
§ 5.2.7 of the standard says:
The result of the expression dynamic_cast(v) is the result of converting the expression v to type
T. T shall be a pointer or reference to a complete class type, or “pointer to cv void”. Types shall not be
defined in a dynamic_cast. The dynamic_cast operator shall not cast away constness (5.2.11).
If T is a pointer type, v shall be an rvalue of a pointer to complete class type, and the result is an rvalue of
type T. If T is a reference type, v shall be an lvalue of a complete class type, and the result is an lvalue of
the type referred to by T.
If the type of v is the same as the required result type (which, for convenience, will be called R in this
description), or it is the same as R except that the class object type in R is more cv-qualified than the class
object type in v, the result is v (converted if necessary).
If the value of v is a null pointer value in the pointer case, the result is the null pointer value of type R.
If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a
pointer to the unique B sub-object of the D object pointed to by v. Similarly, if T is “reference to cv1 B”
and v has type “cv2 D” such that B is a base class of D, the result is an lvalue for the unique60) B sub-object
of the D object referred to by v. In both the pointer and reference cases, cv1 shall be the same cvqualification
as, or greater cv-qualification than, cv2, and B shall be an accessible unambiguous base class
of D. [Example:
struct B {};
struct D : B {};
void foo(D* dp)
{
B* bp = dynamic_cast(dp); // equivalent to B* bp = dp;
}
—end example]
Otherwise, v shall be a pointer to or an lvalue of a polymorphic type (10.3).
And to make a type polymorphic, it needs a virtual function, as per § 10.3:
Virtual functions support dynamic binding and object-oriented programming. A class that declares or
inherits a virtual function is called a polymorphic class.
So the reason why is "because the standard says so." That doesn't really tell you why the standard says so though, but the other answers cover that well I think.
So, I guess my question is: If what I'm doing really works, why do I need a virtual function to give my class a vtable? Why can't I declare the class a "runtime type" or something without virtual functions?
The presence of a virtual function is what makes a class polymorphic in C++. dynamic_cast<> only works with polymorphic classes. (The compiler will reject a dynamic cast on a non-polymorphic object.)
Polymorphism has a cost, both in time and in space (memory). Calls to virtual functions are now indirect, typically implemented in terms of a virtual table. In some critical places, those costs are simply unacceptable. So the language provides means of avoiding these costs.
Similar concepts exist elsewhere in the language. The underlying principle is that if you don't want to use some high-falutin' feature you shouldn't have to pay for the fact the some people do want to use it.
dynamic_cast requires the type to be polymorphic, and without any virtual methods (or at least a virtual destructor) a type is not (run-time) polymorphic. Simple inheritance is not enough. The run-time type information used by dynamic_cast is stored alongside the vtable if remember correctly.
There are two main reasons. The first is that there's just no use case for it. The point of inheritance is virtual functions. If you're not using virtual functions, don't use inheritance.
The second is that it's very complex to actually implement dynamic_cast that works without virtual functions due to the C++ compilation model. The only way to realistically implement dynamic_cast is to operate on the virtual table- a binary blob of data is typeless. You could define a class and then only dynamic_cast it in one TU- now one TU thinks the class has a vtable and one doesn't. That would be instant bad. Allowing dynamic_cast on classes that do not already have virtual functions would be, well, export, which means "Exceedingly difficult to implement".
As others have said, you need at least one virtual function to make a class polymorphic. Why this matters is that dynamic_cast itself is a polymorphic operation! Given a base class pointer, it returns different results based on the actual object it is called on.
C++ has a "don't pay for what you don't need" philosophy, thus the vtable (or whatever mechanism the compiler uses) is not provided unless there's a need as determined by the presence of a virtual function. Evidently the designers of C++ thought this was a reasonable requirement for the proper operation of dynamic_cast or they would have provided a way to generate a vtable without it.
[EDIT] According to the comments (people way smarter than me) my answer is completely wrong. However, make your destructors virtual anyway. [/EDIT]
In C++, I consider upcasting to a base type is only safe if the destructor is virtual. Technically it's safe, but in reality, you almost always want a virtual destructor. For instance:
class Base {
int thingy;
};
class Derived : Base{
int *array;
Derived() {array = new int[100];}
~Derived() {delete [] array;}
};
int main() {
std::auto_ptr<Base> obj(dynamic_cast<Base*>(new Derived));
}
In this example, when obj goes out of scope, the auto_ptr automatically calls the Base's destructor, but does not call the Derived deconstructor because the type is a Base, not a Derived. [Edit: corrections] This causes Undefined behaviour (at the very best, it causes a memory leak). I haven't any idea why C++ doesn't require a virtual destructor to compile down casts, it really should.

Why use static_cast rather than dynamic_cast in here?

I copy the following text from the book More Effective C++.
Item 31: Making functions virtual with respect to more than one object.
class GameObject { ... };
class SpaceShip: public GameObject { ... };
class SpaceStation: public GameObject { ... };
class Asteroid: public GameObject { ... };
The most common approach to double-dispatching returns us to the unforgiving world of virtual function emulation via chains of if-then-elses. In this harsh world, we first discover the real type of otherObject, then we test it against all the possibilities:
void SpaceShip::collide(GameObject& otherObject)
{
const type_info& objectType = typeid(otherObject);
if (objectType == typeid(SpaceShip)) {
SpaceShip& ss = static_cast<SpaceShip&>(otherObject);
process a SpaceShip-SpaceShip collision;
}
else if (objectType == typeid(SpaceStation)) {
SpaceStation& ss =
static_cast<SpaceStation&>(otherObject);
process a SpaceShip-SpaceStation collision;
}
...
}
Here is the question:
Q1> Why we use static_cast here rather than obvious dynamic_cast?
Q2> Are they same in this case?
thank you
// updated //
In fact, I am more interested in question 2.
For example,
class base {};
class subclass : public base {};
base *pSubClass = new subclass;
subclass *pSubClass1 = static_cast<subClass*> (pSubClass);
// does the static_cast do the job correctly in this case although I know we should use dynamic_cast here?
For the record, here is the idiomatic way of doing that:
void SpaceShip::collide(GameObject& otherObject)
{
if (SpaceShip* ss = dynamic_cast<SpaceShip*>(&otherObject)) {
// process a SpaceShip-SpaceShip collision;
}
else if (SpaceStation* ss = dynamic_cast<SpaceStation*>(&otherObject)) {
// process a SpaceShip-SpaceStation collision;
}
// ...
}
It's shorter, exhibits identical performance characteristics, and again, most importantly, is idiomatic C++ that won't make other programmers scratch their heads and wonder what the point is.
EDIT (in response to the OP's edit):
Yes, that is well defined behavior. Here's what the C++03 standard says, §5.2.9/8:
An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists, cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The null pointer value is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.
You've already verified the types yourself, so you don't need to use dynamic_cast. Dynamic_cast will check the types for you automatically.
Why they chose to implement it this way, instead of the more traditional dynamic_cast I can't say, but the behavior of the two options is not necessarily the same. As written, that code only considers the actual type of the parameter, while dynamic_cast considers where the parameter falls in an inheritance tree. Consider:
struct Base { virtual ~Base() { } };
struct Intermediate : Base { };
struct Derived : Intermediate { };
int main() {
Intermediate i;
Base* p_i = &i;
Derived d;
Base* p_d = &d;
assert(typeid(*p_i) == typeid(Intermediate)); //1
assert(dynamic_cast<Intermediate*>(p_i)); //2
assert(typeid(*p_d) == typeid(Intermediate)); //3
assert(dynamic_cast<Intermediate*>(p_d)); //4
}
(1) and (2) both pass their assertions, but (3) fails while (4) succeeds. p_d points to a Derived object, so type_id yields information for a Derived object, which will not compare equal to the information for an Intermediate object. But Derived derives from Intermediate, so dynamic_cast will happily convert a pointer to Derived to a pointer to Intermediate.
To put it in terms used in the original question, if otherObject is a Frigate, which derives from SpaceShip, it will not use the "spaceship<->spaceship" collision routine. There's a good chance this is not the intended behavior; you might want Frigate to use that code, but instead you have to manually add an explicit check for that new type.
Of course, if you're only checking against types that are never inherited from, this difference goes away. Or if you just don't want polymorphic behavior (although that would make the heading somewhat misleading). In that case, this might be more performant, but that's a giant implementation detail and I certainly wouldn't put money on it in practice.
Another small, and largely inconsequential, difference occurs if the types are not polymorphic. In my above code, if you remove the virtual destructor from Base, (2) and (4) now exhibit undefined behavior. (1) and (3) remain well defined, but are now worthless; both will fail because typeid(*p_i) will yield information about Base rather than Intermediate like it used to.
This seems like a pretty solid answer. Basically static cast is faster but doesn't do runtime type checking.
Some compilers will generate codes that throws std::bad_cast if dynamic_cast fails. So in this case the two approaches are different. Using dynamic_cast may looks like
try {
SpaceShip& ship = dynamic_cast<SpaceShip&>(otherObject);
// collision logic
return;
} catch (std::bad_cast&) {}
try {
SpaceStation& station = dynamic_cast<SpaceStation&>(otherObject);
// collision logic
return;
} catch (std::bad_cast&) {}
that looks really bad.
First, I think it's important to note that Myers is presenting this code as the first strawman solution for double dispatch before moving on to superior solutions that are not dependent on RTTI.
To answer the second question first, yes, this is equivalent to implementations using dynamic_cast.
static_cast is used here because we have already established that the object is of the targetted type, and thus don't need to pay for run-time checking again.
So why not use dynamic_cast in the first place?
My suspicion is that Myers wrote it this way because this was going to be a chain of an indefinite number of if () checks. He could have done something like #ildjarn suggests, but that would have involved declaring a new variable for every type he wanted to check it against. My suspicion is he just liked the aesthetics of what he put up better.
Maybe I'm mistaken, but ... my understanding is that all rtti implementations involve some kind of lookup/search to find the type of an object passed to dynamic_cast or typeinfo.
Barring quantum effects, this search must take a measurable number of cycles to complete and, in the OP's code, the search result is cached, while in the dynamic_cast examples the search is repeated in each conditional.
Therefore the cached version must be faster. Keeping in mind caveats about premature optimization, I think it is also easy on the eye.
Nicht war?
PS: Tried this and it doesn't work. Hmmm. Anybody suggest why?