I am currently working to implement a "safe cast" for a small subset of my project, using a meta-type system, because a) RTTI and dynamic_cast was intentionally disabled in my project environment and b) such functionality would greatly simplify and clarify the code.
In my project, there is a multi-level class hierarchy, with multiple inheritance in some cases. There is, however, a single “root” class that is virtually inherited, directly or indirectly, by all other classes. (This is the only virtual inheritance in the hierarchy.) Is there a safe, legal way to perform downcasts in such a hierarchy, without using RTTI / dynamic_cast? I have searched the web and considered several approaches, but none seem to hit the mark. In my case, the class hierarchy is known - every object knows their (meta)type and their ancestors, essentially via the use of tags/enums. (Is there other information, such as base class initialization order, that must be known to implement a “safe cast”?)
One approach I tried was to downcast via a virtual function call that returns the “this” pointer. From what I've read (and my own experience), I understand that the "this" pointer in C++ is of the type corresponding to the class in which the member function is defined, with the cv qualifications from the member function applied to it. (As described here => Type of 'this' pointer). I tried to get the pointer having the dynamic (run-time) type of an object by returning the "this" pointer. Since I want all my objects to implement this function, I make it a pure virtual function in my base class, and then I define it in my derived classes using a covariant return type (a pointer of the implementing class). Dispatching through the base pointer works as expected, but the returned pointer seems to have the type based on the invoking pointer, not the type of the invoked implementation.
Here's code that illustrates the problem:
#include <iostream>
struct Base {
virtual ~Base(){}
virtual Base* foo( void ) = 0 ;
} ;
struct Derived : public Base { // <= XXX
Derived* foo( void ){ std::cout << "In Derived::foo()" << std::endl ; return this ; }
void foo2( void ) { std::cout << "In Derived::foo2()" << std::endl ; }
} ;
int main ( int argc, char* argv[] ) {
Base* base = new Derived ;
base->foo( ) ; // Dispatches to Derived::foo()
// Derived* derived = base->foo( ) ; // PROBLEM!
Derived* derived = static_cast< Derived* >( base->foo( ) ) ; // Works, as long as inheritance at XXX is non-virtual
derived->foo2( ) ;
delete base ;
return 0 ;
}
As written, the above code compiles and runs without problems. But if I uncomment the line labeled "PROBLEM" and comment out the line below it, I get the following compiler error (when using g++ version 4.83 in a cygwin environment - NOT my ultimate target environment):
test.cpp: In function ‘int main(int, char**)’:
test.cpp:13:34: error: invalid conversion from ‘Base*’ to ‘Derived*’ [-fpermissive]
Derived* derived = base->foo( ) ; // PROBLEM!
^
I have tried this with versions of icc, clang, and g++ at http://gcc.godbolt.org/ and received similar results, so I believe this is not a compiler bug and I am missing something very fundamental.
What is the type of a returned "this" pointer from a virtual member function definition with a covariant return type? When I invoke foo() via a Base*, I obviously execute the implementation of Derived::foo(), and that implementing member function is declared to return a Derived*. So why is a downcast necessary to perform the assignment from the returned pointer? Using static_cast is a problem because I do have a virtually inherited base class in my project code, and static_cast will fall down on that. I am under the impression that using a C-style cast or reinterpret_cast for a downcast from a base class is not safe (at least, in the general case), as they are “blind” to object data / virtual table layout. Can these be made “safe” by following some rules/restrictions?