All you needed to do was declare RB
without the : public RA
(oh, and also add ;
to the end of your class definitions):
class RA;
class A {
public:
virtual RA* Foo();
};
class RB;
class B {
public:
virtual RB* Foo();
};
// client includes somewhere.h
class RA {};
class RB : public RA {};
int main ()
{
return 0;
}
However, this doesn't solve the specific problem described nicely in the answer by user1332054.
Some of the other answers appear to show some misconceptions that I'd like to dispel:
Forward declaring is useful even when when we know that the definition is not likely to be included. This allows us to do a lot of type-deduction in our libraries that make them compatible with many other established libraries without including them. Including libraries unnecessarily leads to too many nested includes that can explode the compile time. It's good practice to make your code compatible when appropriate, and to include as little as possible.
Typically you can define a class with pointers to classes that have only been declared and not defined. Example:
struct B;
struct A
{
B * b_;
B * foo ()
{
return b_;
}
B & foo (B * b)
{
return *b;
}
};
int main ()
{
return 0;
}
The above compiles fine, because the compiler doesn't need to know anything about B.
An example where it might be a bit harder to realise that the compiler needs more information:
struct B;
struct A
{
B * foo ()
{
return new B;
}
};
The above problem is because new B
invokes the B::B()
constructor which hasn't been defined yet. Also:
struct B;
struct A
{
void foo (B b) {}
};
Here foo
must call the copy constructor for b
, which also hasn't been defined yet. Lastly:
struct B;
struct A
{
B b;
};
Here we have implicitly defined A
with the default constructor, which calls the default constructor of call its members,b
, which hasn't been defined yet. I think' you get the point.
So, in reference to the more general problem, described by user1332054, I honestly don't understand why it's not possible to use pointers to undefined classed in an inherited virtual function.
More broadly though, I think that you're making it more difficult for yourself by defining your classes instead of only declaring them. Here's an example where you get to DoCleverStuff
with your classes in your library before you've defined any of your classes at all:
// Just declare
class RA;
class RB;
class A;
class B;
// We'll need some type_traits, so we'll define them:
template <class T>
struct is_type_A
{
static constexpr bool value = false;
};
template <>
struct is_type_A <A>
{
static constexpr bool value = true;
};
template <class T>
struct is_type_B
{
static constexpr bool value = false;
};
template <>
struct is_type_B <B>
{
static constexpr bool value = true;
};
#include <type_traits>
// With forward declarations, templates and type_traits now we don't
// need the class definitions to prepare useful code:
template<class T>
typename std::enable_if<is_type_A<T>::value, RA *>::type
DoCleverStuff (T & t)
{
// specific to A
return t.fooRet();
}
template<class T>
typename std::enable_if<is_type_B<T>::value, RB *>::type
DoCleverStuff (T & t)
{
// specific to B
return t.fooRet();
}
// At some point the user *does* the include:
class RA
{
int x;
};
class RB : public RA
{
int y;
};
class A
{
public:
virtual RA * fooRet()
{
return new RA;
}
};
class B : public A
{
public:
virtual RB * fooRet()
{
return new RB;
}
};
int main ()
{
// example calls:
A a;
RA * ra = DoCleverStuff(a);
B b;
RB * rb = DoCleverStuff(b);
delete ra;
delete rb;
return 0;
}