According to this, void* has no RTTI information, therefore casting from void* is not legal and it make sense.
If I remember correctly, dynamic_cast from void* was working on gcc.
Can you please clarify the issue.
dynamic_cast works only on polymorphic types, i.e. classes containing virtual functions.
In gcc you can dynamic_cast to void* but not from:
struct S
{
virtual ~S() {}
};
int main()
{
S* p = new S();
void* v = dynamic_cast<void*>(p);
S* p1 = dynamic_cast<S*>(v); // gives an error
}
In 5.2.7 - Dynamic cast [expr.dynamic.cast] it says that for dynamic_cast<T>(v):
If T is a pointer type, v shall be an rvalue of a pointer to complete class type
If T is a reference type, v shall be an lvalue of a complete class type (thanks usta for commenting on my missing this)
...
Otherwise, v shall be a pointer to or an lvalue of a polymorphic type
So, no, a (void*) value is not allowed.
Let's think about what your request might mean: say you've got a pointer that's really to a Derived1*, but the code dynamic_cast-ing only knows it's a void*. Let's say you're trying to cast it to a Derived2*, where both derived classes have a common base. Superficially, you might think all the pointers would point to the same Base object, which would contain a pointer to the relevant virtual dispatch table and RTTI, so everything could hang together. But, consider that derived classes may have multiple base classes, and therefore the needed Base class sub-object might not be the one to which the Derived* - available only as a void* - is pointing. It wouldn't work. Conclusion: the compiler needs to know these types so it can perform some adjustment to the pointers based on the types involved.
Derived1* -----> [AnotherBase]
[[VDT]Base] <-- but, need a pointer to start of
[extra members] this sub-object for dynamic_cast
(Some answers talk about the need for the pointer you're casting from to be of a polymorphic type, having virtual functions. That's all valid, but a bit misleading. As you can see above, even if the void* is to such a type it still wouldn't work reliably without the full type information, as the real problem is that void* is presumably pointing to the start of the derived object, whereas you need a pointer to the base class sub-object from which the cast-to type derives.)
It is true that void* can't be dynamically_casted from.
You are probably mis-remembering.
With g++ 4.5 and the following code
struct A {
virtual ~A();
};
int main() {
A a;
void *p = &a;
A* pa = dynamic_cast<A*>(p);
}
I get the following error:
cannot dynamic_cast 'p' (of type 'void*') to type 'struct A*' (source is not a pointer to class)
I guess you confuse with dynamic_cast to void*. That is legal and obtains the pointer to the most derived class object.
dynamic_cast from void* is illegal - the type casted from must be polymorphic - contain at least one virtual function (virtual destructor counts too).
To add to Tony's nice answer, this little code snippet helps me for some reason. First, we establish a simple hierarchy. Then, we see if dynamic_cast can "survive" a static_cast. Before this experiment I thought "the run time type information is there, dynamic cast should figure it out." Now I realize "dynamic_cast must have to look up its information based on some tables the compiler is aware of, so it can't have some magical power."
#include <iostream>
#include <cassert>
using namespace std;
class A {
protected:
virtual void foo() { cout << "A" << endl; }
};
class B1 : public A {
private:
virtual void foo() override { cout << "B1" << endl; }
};
class B2 : public A {
public:
virtual void foo() override { cout << "B2" << endl; }
};
int main(int argc, char **argv) {
B1 b1;
// undefined behavior even though dynamic_cast didn't return null
dynamic_cast<B2*>(
static_cast<B2*>(
static_cast<A*>(&b1)))->foo();
// dynamic_cast returns null here though
assert (!dynamic_cast<B2*>
(static_cast<A*>
(static_cast<B2*>
(static_cast<A*>(&b1)))));
}
You can cast a pointer to polymorphic type to void *, but not vice versa.
Related
My C++ is a bit rusty and I don't remember everything in the standard.
I have a void*. In one specific function it is either a class that inherits Alpha or one that inherits Beta. Both base classes have virtual functions. However I can't seem to tell which is which
class Alpha {
public:
virtual void Speak() { printf("A"); }
};
class Beta {
public:
virtual void Speak() { printf("B"); }
};
int main(){
//BAD CODE WILL PRINT TWICE
void *p = new Alpha;
Alpha*a = dynamic_cast<Alpha*>((Alpha*)p);
Beta*b = dynamic_cast<Beta*>((Beta*)p);
if(a)
a->Speak();
if(b)
b->Speak();
return 0;
}
How do I figure out which class is which? There are 100's of classes in this codebase that gets converted to void. Most of them inherit 5 base classes however I'm not eager to find out. Is the only solution inheriting from something like class Dummy {public: virtual void NoOp(){}}; and cast to Dummy before using dynamic cast? Is this safe? I'm hoping there's a better solution but I can't think of anything else.
The only thing you can do with a void* pointer is to cast it back to exactly the same type as the pointer that was cast to void* in the first place. The behaviour on doing anything else is undefined.
What you could do in your case is define
class Base
{
public:
virtual ~Base() = default; // make me a polymorphic type and make
// polymorphic delete safe at the same time.
};
and make this the base class for Alpha and Beta. Then pass a Base* pointer around rather than a void* one, and take your dynamic_casts directly on p.
Note further that if you declared virtual void Speak() = 0; in Base, then your code in main would become simply
int main(){
Base* p = new Alpha;
p->Speak();
delete p; // ToDo - have a look at std::unique_ptr
}
As a rule of thumb, casts of any kind are undesirable.
The expression Alpha*a = dynamic_cast<Alpha*>((Alpha*)p); first casts p to Alpha* with an explicit c style cast. Then, that resulting Alpha* is passed through dynamic_cast<Alpha*>. Using dynamic_cast<T*> on a T* pointer (a pointer of the same type as you are trying to cast to) will always provide the input pointer. It cannot be used to confirm that the pointer is valid. From cppreference for dynamic_cast<new_type>(expression) :
If the type of expression is exactly new_type or a less cv-qualified version of new_type, the result is the value of expression, with type new_type.
As a result, the code will always compile and run and the type system will not protect you. But the resulting behavior is undefined. In the case of Beta*b = dynamic_cast<Beta*>((Beta*)p); you tell the compiler to trust that p is a Beta* but this is not true. Dereferencing the resulting pointer is undefined behavior and dynamic_cast cannot protect you from this mistake.
If you try to remove the explicit type conversion, you will get a compiler error. dynamic_cast requires a pointer or reference to a complete type, and void is not a complete type. You will have to find a way to track the actual type pointed to yourself and explicitly convert p to that pointer type before using dynamic_cast. Though at that point, if you already know the type to cast to, it may no longer be necessary.
Consider using a common base type instead or maybe using std::variant or std::any if need be.
If you use a C-style cast to convert to Alpha*, similar to a static_cast before using dynamic cast, then the dynamic cast as no effect. here your code runs because the two classes have the same interface but in reality this is undefined behaviour.
Usually, you want to use dynamic cast to upcast/downcast from/to a base class to/from it's derived class.
For example, if we add a base interface, then convert the void * pointer to this base class and then use dynamic cast to attempt Up-casting, the code works as expected and only print once.
#include <stdio.h>
class Speaker {
public:
virtual void Speak() = 0;
};
class Alpha: public Speaker {
public:
virtual void Speak() { printf("A"); }
};
class Beta: public Speaker {
public:
virtual void Speak() { printf("B"); }
};
int main(){
void *p = new Alpha;
// Convert to base type, using static_cast
Speaker *s = static_cast<Speaker *>(p);
// Attempt Upcasts
Alpha*a = dynamic_cast<Alpha*>(s);
Beta*b = dynamic_cast<Beta*>(s);
// See if it worked
if (a)
a->Speak();
if (b)
b->Speak();
return 0;
}
Outputs: A
You must know the type from which the void pointer was converted from. If you don't know the dynamic type, then you must create the void pointer from the pointer to base. If you don't know the type of the pointer which the void pointer was created from, then you cannot use the void pointer.
Given that the void pointer was converted from Alpha*, you can convert it back using a static cast:
auto a_ptr = static_cast<Alpha*>(p);
You can then use dynamic_cast to convert to a derived type.
if(auto d_ptr = dynamic_cast<DerivedAlpha*>(a_ptr)) {
// do stuff with DerivedAlpha
In case the dynamic type isn't DerivedAlpha, the dynamic cast would safely return null. You cannot dynamic cast away from a type hierarchy. Since Alpha and Beta are not related by any inheritance structure, they cannot be dynamically casted mutually.
I feel this could be explained more simply than what is said already.
Basically, casting from a raw void*, none of the "smarts" of dynamic_cast can apply because it has nothing to go on, it just gives you back the same pointer. You need to have some shared parent class type or something as your pointer type (*p) so it knows how to read the memory and determine actual type.
In your case, you are "lucking out" that the memory layout of Alpha and Beta is the same so calling speak on Beta* ends up calling Alpha::speak(). As others said this is UB and if the classes were more different you would likely seg-fault or corrupt the stack.
#include<iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
};
class Class2 {
public:
virtual void h() {
cout << "Function h() in Class2\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
p = &object1;
p = (Class1*)&object2;
p->f();//possibly abnormal program termination. but in fact, it will call function h(),Why?
return 0;
}
The code must be wrong in theory because p->f() there is no function f() in the Class2.
But in fact, the code can be run, and it will call Function h() in the Class2, It's so strange,why??
C++ doesn't have many run-time checks for performance reasons, so it will just do what you tell it to do even if it is illogical.
You are telling it to call the virtual method Class1::f on a pointer organised in the memory layout of Class2 and apparently Class2::h aligns with Class1::f so it gets called.
This is not standardised behaviour and may differ between compilers. See Virtual method table.
There is a problem with the code, and as #tkausi commented, the behavior is undefined.
The problem is that you are using C-style cast which is dangerous and should be avoided in C++. From cppreference:
When the C-style cast expression is encountered, the compiler
attempts to interpret it as the following cast expressions, in this
order:
const_cast<new_type>(expression);
static_cast<new_type>(expression), with extensions: pointer or
reference to a derived class is additionally allowed to be cast to
pointer or reference to unambiguous base class (and vice versa) even
if the base class is inaccessible (that is, this cast ignores the
private inheritance specifier). Same applies to casting pointer to
member to pointer to member of unambiguous non-virtual base;
static_cast (with extensions) followed by const_cast;
reinterpret_cast<new_type>(expression);
reinterpret_cast followed by const_cast.
The first choice that satisfies the requirements of
the respective cast operator is selected, even if it cannot be
compiled (see example). If the cast can be interpreted in more than
one way as static_cast followed by a const_cast, it cannot be
compiled. In addition, C-style cast notation is allowed to cast from,
to, and between pointers to incomplete class type. If both expression
and new_type are pointers to incomplete class types, it's unspecified
whether static_cast or reinterpret_cast gets selected.
Thus, in your case the compiler will select reinterpret_cast which will just reinterpret the raw bytes of your object as something else (and it just happens that the first thing in the virtual table of p points to method h).
To make your program safe, you should instead use static_cast or dynamic_cast:
#include<iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
};
class Class2 {
public:
virtual void h() {
cout << "Function h() in Class2\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
p = &object1;
p = static_cast<Class1*>(&object2); // fails to compile
p->f();
return 0;
}
In this case, compilation fails as expected.
Live example: https://godbolt.org/g/noErNr
Note that if we replace static_cast line with p = dynamic_cast<Class1*>(&object2); the code will compile, but p will be set to nullptr at runtime, and trying to call p->f() will result in an error.
The code must be wrong in theory because p->f() there is no function
f() in the Class2.
The code is not only wrong in theory, but it is wrong in pratice too. The thing is that C++ does not hold your hand and does not wrap you in cotton wool, if you do wrong stuff, bad things can happen. Typesafety is your friend, but if you betray it, you are on your own.
In this line:
p = (Class1*)&object2;
you pretend that you could cast a Class2 to a Class1. Anything beyond that is undefined behaviour, which is sloopy speaking a way of saying: c++ does not care what is the output of programs that dont obey the rules.
The way to stay friends with the typesystem is to use static_cast instead of a C-style cast, which would tell you that the cast is not allowed.
Here is some code that illustrates the question:
#include <iostream>
class Base {
};
class Derived : public Base {
};
void doThings(Base* bases[], int length)
{
for (int i = 0; i < length; ++i)
std::cout << "Do ALL the things\n";
}
int main(int argc, const char * argv[])
{
Derived* arrayOfDerived[2] = { new Derived(), new Derived() };
doThings(arrayOfDerived, 2); // Candidate function not viable: no known conversion from 'Derived *[2]' to 'Base **' for 1st argument
// Attempts to work out the correct cast
Derived** deriveds = &arrayOfDerived[0];
Base** bases = dynamic_cast<Base**>(deriveds); // 'Base *' is not a class
Base** bases = dynamic_cast<Base**>(arrayOfDerived); // 'Base *' is not a class
// Just pretend that it should work
doThings(reinterpret_cast<Base**>(arrayOfDerived), 2);
return 0;
}
Clang produces the errors given in the comments. The question is: "Is there a correct way to cast arrayOfDerived to something that doThings can take?
Bonus marks:
Why does clang produce the errors "'Base *' is not a class" on the given lines? I know that Base* isn't a class it's a pointer. What is the error trying to tell me? Why has dynamic_cast been designed so that in dynamic_cast<T*> the thing T must be a class?
What are the dangers of using the reinterpret_cast to force everything to work?
Thanks as always :)
No. What you’re asking for here is a covariant array type, which is not a feature of C++.
The risk with reinterpret_cast, or a C-style cast, is that while this will work for simple types, it will fail miserably if you use multiple or virtual inheritance, and may also break in the presence of virtual functions (depending on the implementation). Why? Because in those cases a static_cast or dynamic_cast may actually change the pointer value. That is, given
class A {
int a;
};
class B {
string s;
};
class C : public A, B {
double f[4];
};
C *ptr = new C();
unless the classes are empty, ptr == (A *)ptr and/or ptr == (B *)ptr will be false. It only takes a moment to work out why that must be the case; if you're using single inheritance and there is no vtable, it’s guaranteed that the layout of a subclass is the same as the layout of the superclass, followed by member variables defined in the subclass. If, however, you have multiple inheritance, the compiler must choose to lay the class out in some order or other; I'm not sure what the C++ standard has to say about it (you could check - it might define or constrain the layout order somehow), but whatever order it picks, it should be apparent that one of the casts must result in a change the pointer value.
The first error in your code is that you are using a syntax that should be avoided:
void doThings(Base* bases[], int length)
if you look at the error message, it tells you that bases is actually a Base**, and that is also what you should write if you really want to pass such a pointer.
The second problem has to do with type safety. Imagine this code, where I reduced the array of pointers to a single pointer:
class Base {...};
class Derived1: public Base {...};
class Derived2: public Base {...};
Derived1* p1 = 0;
Base*& pb = p1; // reference to a pointer
pb = new Derived2(); // implicit conversion Derived -> Base
If this code compiled, p1 would now suddenly point to a Derived2! The important difference to a simple conversion of a pointer-to-derived to a pointer-to-base is that we have a reference here, and the pointer in your case is no different.
You can use two variations that work:
Base* pb = p1; // pointer value conversion
Base* const& pb = p1; // reference-to-const pointer
Applied to your code, this involves copying the array of pointers-to-derived to an array of pointers-to-base, which is probably what you want to avoid. Unfortunately, the C++ type system doesn't provide any different means to achieve what you want directly. I'm not exactly sure about the reason, but I think that at least according to the standard pointers can have different sizes.
There are two things I would consider:
Convert doThings() to a function template taking two iterators. This follows the spirit of the STL and allows calls with different pointer types or others that provide an iterator interface.
Just write a loop. If the body of the loop is large, you could also consider extracting just that as a function.
In answer to your main question, the answer is not really,
because there's no way of reasonably implementing it. There's
no guarantee that the actual addresses in the Base* will be
the same as those in the Derived*, and in fact, there are many
cases where they aren't. In the case of multiple inheritance,
it's impossible that both bases have the same address as the
derived, because they must have different addresses from each
other. If you have an array of Derived*, whether it be
std::vector<Derived*> or a Derived** pointing to the first
element of an array, the only way to get an array of Base* is
by copying: using std::vector<Derived*>, this would look
something like:
std::vector<Base*> vectBase( vectDerived.size() );
std::transform(
vectDerived.cbegin(),
vectDerived.cend(),
vectBase.begin(),
[]( Derived* ptr ) { return static_cast<Base*>( ptr ); } );
(You can do exactly the same thing with Derived** and
Base**, but you'll have to tweek it with the known lengths.)
As for your "bonus" questions:
Clang (an I suspect every other compiler in existance),
produces the error Base* is not a class because it isn't
a class. You're trying to dynamic_cast between Base**
and Derived**, the pointed to types are Base* and
Derived*, and dynamic_cast requires the pointed to types
to be classes.
The only danger with reinterpret_cast is that it won't
work. You'll get undefined behavior, which will possibly
work in a few simple cases, but won't work generally. You'll
end up with something that the compiler thinks is a Base*,
but which doesn't physically point to a Base*.
I've wrote a piece of code, but I am confused with its output:
#include <iostream>
using namespace std;
class B{
public:
virtual void foo() {cout << "B::foo" << endl;}
};
class D:public B{
public:
virtual void foo() {cout << "D::foo" << endl;}
void disp() {cout << "D::disp" << endl;}
};
void func(B *pb){
D *pd1 = static_cast<D*>(pb);
pd1->foo();
pd1->disp();
}
int main(int argc, char *argv[])
{
B* pb = new B();
func(pb);
return 0;
}
The output is:
B::foo
D::disp
But as far as I know, pb points to type B. And there's no function named disp() in it? So, why could it get access to disp() function in class D?
Since disp() doesn't access any of the members of the class, it is in principle the same as if it were declared in the global namespace instead of in class, so there are no negative side effects to calling it, even though the instance is not of the right class.
What are you doing is downcasting a pointer of a base class to a pointer of the derived class, even though it was not initialized as such. If disp() tried to access class members that were in D but not in B, you would probably run into segfaults.
Bottom line: don't use static_cast for downcasting unless you're absolutely sure the pointer is actually pointing to an instance of the derived class. If you're unsure, you can use dynamic_cast which fails in the event of mismatch (but there is the overhead of RTTI, so avoid it if you can).
dynamic_cast will return nullptr if the cast is incorrect or throw a std::bad_cast
exception if it casts references, so you will know for sure why it fails instead of possible memory corruption bugs.
The line:
D *pd1 = static_cast<D*>(pb);
Will make the cast regardless if the source pointer is B* or D*. In your case the result will be pointer that points to an object of a wrong type. Method disp will work because it is not using any data member or virtual function of the class D. In more complex case this will lead to unstable behaviour or a crash.
Your objects are polimorphic. You should use the dynamic_cast instead.
What's important in this context, I believe, is that the member function disp() isn't tucked away inside all objects of type D. It's a single function that exists in one place. And whether any object will try to call call disp() is decided by the code.
Your static_cast will produce what the compiler considers a pointer to D regardless of what pointer you pass it. And once you have a pointer to D, the compiler will let you attempt to call disp().
Put another way, static_cast will not protect you from casting a pointer incorrectly.
You did something very bad when you cast a pointer to an object allocated as a B to a pointer to a derived class D. Here's what the standard says, emphasis mine:
5.2.9 Static Cast
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 invoked undefined behavior by doing that static_cast. The compiler and runtime can do anything and still be compliant when the program invokes undefined behavior.
You need to understand the difference between the various types of C++ casts.
You're using a static cast here, which is the same as saying, "I don't really care if it's actually of that type or not - just try your best".
In this case, you want to know if your pointer is actually of the derived type you're casting it to. You should use a dynamic_cast. This cast will succeed only if the pointer is of the correct type. That means, that if it fails, it will return a NULL pointer.
The behavior you're seeing is what happens when you don't use the right cast for the job, which, while you can try to explain it, is something you really should avoid because it falls in the domain of undefined behavior. In other words, you cannot expect the same side effects across compilers or even different versions of the same compiler. In other words, avoid it.
I have a template class that will bundle some information with a type:
template <typename T>
class X
{
int data[10]; // doesn't matter what's here really
T t;
public:
// also not terribly relevant
};
Then lets say we have a Base class and Derived class:
class Base {};
class Derived : public Base {};
I'd like to be able to do something like this (but don't think I can):
void f(shared_ptr<X<Base> > b);
shared_ptr<X<Derived> > d(new X<Derived>);
f(d);
Is there a way that I can make pointers to X<T> convertible to pointers of X<Y> if T* is convertible to Y*?
Rephrasing the question to a more general: is it possible to convert pointers from unrelated types if the unrelated types are themselves convertible?, the answer is not. And different template instantiations define different unrelated types.
You can implicitly convert (see note below) a pointer to a derived object to a pointer of the base object (some restrictions apply due to access or ambiguity) or you can make an explicit conversion from a base pointer to a derived pointer (again with some restrictions). You can also convert back and forth to void*, but converting to an unrelated type ends up with undefined behavior.
There are clear reasons for pointers not being convertible from unrelated types. The first of which is that converting one pointer type to another pointer type will not convert the pointed memory. That is, you can convert an int to a double, but converting a pointer to an int to a pointer to double would make the compiler believe that there are 8 bytes of memory at the pointed address while in fact only 4 bytes are present (assuming 32 or 64 bit architecture with 32 bit integers and 64 bit doubles). Dereferencing the pointer will surely end bad.
Notes from the standard:
Section 4.10 of the standard deals with pointer conversions, first paragraph deals with null-pointer conversions, second paragraph with void pointer conversions. The third paragraph states that you can convert a pointer to type D to a pointer of type B as long as B is a base class of D. No other pointer conversion is defined there. In Section 5.4 where explicit conversions are specified, paragraph 7 deals with pointer conversions and only adds the possible explicit conversion from B* to D* in several situations as long as B is a base of B.
G'day,
Have you thought about why you want to do this?
Just coming back to the basics that inheritance is for specialising behaviour for different types of derived objects, e.g. the draw() function declared in a base class called Shape would produce different output for a derived class Square compared to a derived class Circle.
Templates should be used when you want the same sort of behaviour irrespective of the type, e.g. the pop function in a stack of integers behaves the same way as the pop function in a stack of floats behaves the same way as the pop function in a stack of objects of type Foo.
HTH
cheers,
While rethinking on the question I just realized of another problem you have. Polymorphic behavior can only be achieved through pointers and/or references. Trying to do so with values produces what is called slicing, where the original derived object is converted (copied) into a base object loosing all the extra information.
The common example of slicing is this one:
class Base
{
public:
virtual void polymorphic() {
std::cout << "Base" << std::endl;
}
};
class Derived : public Base
{
public:
virtual void polymorphic() {
std::cout << "Derived" << std::endl;
}
};
void f( Base b ) {
b.polymorphic();
}
int main() {
Derived d;
f( d ); // prints Base
}
Now, the original question deals with conversion of pointer types and I answered before. I believe that your code presents a variant of the slicing problem. We can play a little with the undefined behavior of forcing pointer conversions from unrelated types:
// same Base and Derived definitions as above
template <typename T>
struct Holder
{
T data;
};
int main() {
Holder<Derived> derived;
Holder<Derived>* derived_ptr = &derived;
Holder<Base>* base_ptr = reinterpret_cast<Holder<Base>* >( derived_ptr ); // undefined!!!
std::cout << "derived_ptr=" << derived_ptr << ", base_ptr=" << base_ptr << std::endl;
base_ptr->data.polymorphic(); // prints Base
}
First of all, this is undefined behavior according to the standard... but bear with me. We can try to work what a common compiler could implement. We have an object of type Holder, and a pointer to it (derived_ptr), now we reinterpret the pointer as a pointer to Holder. At this point base_ptr and derived_ptr have the same value, they point to the same address (check the output).
In the last step we access the data field of the Holder stored at &derived, and we call the virtual function * polymorphic()* on it... but in Holder, the data field is not a reference or pointer, it is an element of type Base, so the compiler can decide that it will call the Base version of it, and it so does in g++ 4.0.
This is all undefined behavior, so try it with your compiler of choice, it may end up with the same or different outcomes.
If you change the template definition to store a pointer, then you get the expected polymorphic behavior. That is, expected in this case, knowing the memory footprint of the objects at hand. Not that you can expect anything from the unexpected undefined behavior.
EDIT:
What you are trying to do is possible; you can use the convertibility test and the static assertion provided by the boost libraries to generate a compile-time error in case of type incompatibility.
Here is an example:
#include <string>
#include <iostream>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/static_assert.hpp>
template<typename MyType>
class MyTypeInfo{
public:
MyType* myType;
MyTypeInfo(MyType* _myType){
myType = _myType;
}
template <typename DestinationType>
operator MyTypeInfo<DestinationType>() {
BOOST_STATIC_ASSERT((boost::is_convertible<MyType,DestinationType>::value));
MyTypeInfo<DestinationType> ret((DestinationType*)myType);
return ret;
}
};
class Base{
public:
virtual void do_something(){
std::cout << "Base" << std::endl;
}
};
class Derived : public Base{
public:
virtual void do_something(){
std::cout << "Derived" << std::endl;
}
};
class NonDerived{
};
int _tmain(int argc, _TCHAR* argv[])
{
// this one is ok
MyTypeInfo<Derived> d(new Derived());
MyTypeInfo<Base> b = (MyTypeInfo<Base>)d;
b.myType->do_something();
// this one fails to compile
//MyTypeInfo<NonDerived> nd(new NonDerived());
//MyTypeInfo<Base> b2 = (MyTypeInfo<Base>)nd;
//b2.myType->do_something();
return 0;
}
If you don't want or cannot use the boost libraries, you can refer to Andrei Alexandrescu's wonderful book "Modern C++ Design" for a possible implementation of the convertibility test and the static assertion macro.
I don't want to point out the obvious but is there any reason you can't make the function a template ? I.e.
template <typename T>
void f(shared_ptr<T> b);
shared_ptr<X<Derived> > d(new X<Derived>);
f(d);