By returning foo_t
by value, you only return the foo_t
part of the object, with type foo_t
- this is sometimes known as slicing.
Since there is no bar_t
object, the cast is invalid, and using the resulting pointer gives undefined behaviour.
To retain polymorphism, you'll have to return a pointer or reference to an object that still exists after the function returns - that is, not to a local variable. First of all, the base class will need a virtual destructor, so derived classes can be safely managed using a pointer to the base:
// I'll get rid of the weird C-style typedef for readability
struct foo {
virtual ~foo() = default;
};
Now you can allocate an object of the derived type, and return a smart pointer to the base type:
std::unique_ptr<foo> giveMeMyBar() {
auto ret = std::make_unique<bar>();
ret->value = 5;
return ret;
}
Now you can safely cast the type back:
auto foo = giveMeMyBar();
bar * ptr = dynamic_cast<bar*>(foo.get()); // pointer to returned object
bar & ref = dynamic_cast<bar&>(*foo); // reference to returned object
bar copy = dynamic_cast<bar&>(*foo); // copy of returned object
If the type was wrong, then the pointer cast will yield a null pointer, while the reference casts will throw an exception.
Other, less safe casts are available. The C-style cast that you used is the nuclear option, forcing the compiler to accept the conversion in whatever way it can. That often leads to undefined behaviour, so only use it when you're absolutely sure that's what you want.
I came from java
Then you will have to unlearn what you have learned. C++ has a very different object model.
casting is really easy there
But at a price: all java objects are polymorphic, and are accessed indirectly through managed references. C++ allows you the choice of more efficient, but sometimes less convenient, value types.