74

I know that I can do:

class Foo;

but can I forward declare a class as inheriting from another, like:

class Bar {};

class Foo: public Bar;

An example use case would be co-variant reference return types.

// somewhere.h
class RA {}
class RB : public RA {}

... and then in another header that doesn't include somewhere.h

// other.h
class RA;

class A {
 public:
  virtual RA* Foo();  // this only needs the forward deceleration
}

class RB : public RA; // invalid but...

class B {
 public:
  virtual RB* Foo();  // 
}

The only information the compiler should need to process the declaration of RB* B:Foo() is that RB has RA as a public base class. Now clearly you would need somewhere.h if you intend to do any sort of dereferencing of the return values from Foo. However, if some clients never calls Foo, then there is no reason for them to include somewhere.h which might significantly speed compilation.

BCS
  • 67,242
  • 64
  • 175
  • 277
anon
  • 36,629
  • 47
  • 184
  • 286
  • 2
    @Pace Co-variant reference return types. (See, edit.) But I'm not sure that multiple inheritances doesn't mess that up. – BCS Dec 15 '11 at 16:54
  • Possible duplicate of [Pointer to incomplete class type is not allowed](https://stackoverflow.com/questions/12027656/pointer-to-incomplete-class-type-is-not-allowed) – Archmede Aug 09 '17 at 03:00

5 Answers5

47

A forward declaration is only really useful for telling the compiler that a class with that name does exist and will be declared and defined elsewhere. You can't use it in any case where the compiler needs contextual information about the class, nor is it of any use to the compiler to tell it only a little bit about the class. (Generally, you can only use the forward declaration when referring to that class without other context, e.g. as a parameter or return value.)

Thus, you can't forward declare Bar in any scenario where you then use it to help declare Foo, and it flat-out doesn't make sense to have a forward declaration that includes the base class -- what does that tell you besides nothing?

Joe
  • 38,368
  • 16
  • 103
  • 119
  • 74
    "what does that tell you besides nothing?" Uh, it tells you that the type is a subclass of the base class. That's not nothing. For example, if a function in your base class stumbles upon an object that contains your subclass (as an "other" object), it cannot then pass that object to a function that takes a base class object, because it doesn't know how to cast the subclass to the base class even though it can access all the public members of the subclass. I agree with you that it's impossible to do what this person asked, but I disagree with your assessment that doing it would be useless. – codetaku Feb 26 '16 at 17:32
  • @codetaku: Logged in to upvote your comment (: For anyone seeking a concrete example, see Google's C++ style guide (in the "Cons" section): https://google.github.io/styleguide/cppguide.html#Forward_Declarations – jwd Jun 29 '16 at 22:32
  • 2
    @codetaku - yes! e.g., you want a `static_assert` or `enable_if` on a smart pointer to restrict it to certain types - you'd like to use the smart pointer on an incomplete type ... bam! – davidbak Oct 02 '17 at 17:01
  • 1
    @jwd - I disagree with that Google coding style. Information hiding is useful, we've known at least since [Parnas described it](https://dl.acm.org/citation.cfm?doid=361598.361623). If only we had Ada's package bodies, or `limited private`- but we don't. So for the pimpl idiom a `unique_ptr` to an incomplete type is the best we can do in C++. – davidbak Oct 02 '17 at 17:14
40

Forward declarations are declarations, not definitions. So, anything that requires the declaration of a class (like pointers to that class) need only the forward declaration. However, anything that would require the definition - i.e. would need to know the actual structure of the class - would not work with just the forward declaration.

Derived classes definitely need to know the structure of their parent, not just that the parent exists, so a forward declaration would be insufficient.

Jonathan M Davis
  • 34,779
  • 15
  • 69
  • 99
  • 4
    Do you need the know the structure of a class to correctly handle a pointer to it? As long as you deal exclusively in references you should be fine. – BCS Dec 15 '11 at 16:57
  • 2
    @BCS Yes. As long as you're dealing with pointers, you only need forward declarations, but when you're declaring a derived class, you need the parent's definition, not just its declaration, because you're not dealing with just pointers. – Jonathan M Davis Mar 27 '12 at 06:32
  • 5
    It would constrain the implementation of objects, but I think it should be possible to do a `*D` -> `*B` conversion while only knowing the inheritances structure. I expect it would look like the structures needed to implement virtual base classes. – BCS Mar 27 '12 at 13:55
22

No it is not possible to forward declare inheritance, even if you are only dealing with pointers. When dealing with conversions between pointers, sometimes the compiler has to know the details of the class to do the conversion correctly. This is the case with multiple inheritance. (You could special case some parts parts of the hierarchy that only use single inheritance, but that isn't part of the language.)

Consider the following trivial case:

#include <stdio.h>
class A { int x; };
class B { int y; };
class C: public A, public B { int z; };
void main()
{ 
    C c; A *pa = &c; B *pb = &c; C *pc = &c; 
    printf("A: %p, B: %p, C: %p\n", pa, pb, pc);
}

The output I received (using 32 bit visual studio 2010), is:

A: 0018F748, B: 0018F74C, C: 0018F748

So for multiple inheritance, when converting between related pointers, the compiler has to insert some pointer arithmetic to get the conversions right.

This is why, even if you are dealing only with pointers, you can't forward declare inheritance.

As for why it would be useful, it would improve compile times when you do want to make use of co-variant return types instead of using casts. For example this will not compile:

class RA;
class A             { public: virtual RA *fooRet(); };
class RB;
class B : public A  { public: virtual RB *fooRet(); };

But this will:

class RA;
class A             { public: virtual RA *fooRet(); };
class RA { int x; };
class RB : public RA{ int y; };
class B : public A  { public: virtual RB *fooRet(); };

This is useful when you have objects of type B (not pointers or references). In this case the compiler is smart enough to use a direct function call, and you can use the return type of RB* directly without casting. In this case, usually I go ahead and make the return type RA * and do a static cast on the return value.

user1332054
  • 395
  • 3
  • 6
  • 7
    I know this is an old answer, but IMO this is a much more helpful answer than the higher-voted and accepted ones, that are ignoring the fact that converting a `Foo*` to a `Bar*` (or reference) can be a useful thing to do even with an incomplete type. – EvanED Dec 18 '15 at 01:49
2

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;
}
Elliott
  • 1,331
  • 2
  • 10
  • 26
-1

I don't think it's useful. Consider: you have defined a class, Bar:

class Bar {
public:
    void frob();
};

Now you declare a class Foo:

class Foo;

All you can do with Foo is construct a pointer to it. Now, suppose you add the information that Foo is derived from Bar:

class Foo: public Bar;

What can you now do that you couldn't do before? I think that all you can do is accept a pointer to Foo and cast it into a pointer to Bar, then use that pointer.

void frob(Foo* f) {
    Bar *b = (Bar)f;
    b->frob();
}

However, you must have generated the pointer elsewhere, so you could have just accepted a pointer to Bar instead.

void frob(Bar* b) {
    b->frob();
}
Andrew Aylett
  • 36,202
  • 4
  • 63
  • 92
  • 2
    class Foo: public Bar; tells that dynamic_cast<> will work, doesn't it? I think this would be an important piece of information. – Fabian Apr 25 '17 at 10:54