views:

75

answers:

3

I have two classes, Foo<T> and Bar<T>, derived from Base. Each overrides a method virtual Base* convert(ID) const, where ID is an instance of a type that uniquely identifies a particular instantiation of Foo or Bar (pretend it's an enum). The problem is that Foo::convert() needs to be able to return a Bar instance, and likewise Bar::convert() needs to be able to instantiate Foo. Since they're both templates, this results in a circular dependency between Foo.h and Bar.h. How do I resolve this?

Edit: A forward declaration does not work because the implementation of each method needs the constructor of the other class:

Foo.h:

#include <Base.h>

template<class T> class Bar;

template<class T>
class Foo : public Base { ... };

template<class T>
Base* Foo<T>::convert(ID id) const {

    if (id == BAR_INT)
        return new Bar<int>(value); // Error.

    ...

}

Bar.h:

#include <Base.h>

template<class T> class Foo;

template<class T>
class Bar : public Base { ... };

template<class T>
Base* Bar<T>::convert(ID id) const {

    if (id == FOO_FLOAT)
        return new Foo<float>(value); // Error.

    ...

}

The error is, naturally, "invalid use of incomplete type".

+1  A: 

(Updated) You should be able to handle that the same as with non-template classes. Write your Bar.h like this. (And similarly for Foo.h)

#if !defined(BAR_H_INCLUDED)
#define BAR_H_INCLUDED

template <class T>
class Foo;

template <class T>
class Bar
{
    /// Declarations, no implementations.
}    

#include "Foo.h"

template <class T>
Base* Bar<T>::Convert() {  /* implementation here... */ }
#endif
James Curran
No dice. The classes can't be forward-declared because I need to make use of their members, or at least the constructor, in order to perform the conversion. I get the expected "invalid use of incomplete type".
Jon Purdy
@Jon: See updated post.
James Curran
The solution I worked out myself from KeithB's answer is similar to this, but I don't think this actually compiles because `Foo.h` and `Bar.h` would still need to include one another, so one would come up empty-handed.
Jon Purdy
@Jon: That's why I have the include guard. Bar.h includes Foo.h which attempts to include Bar.h, but gets nothing the second time.
James Curran
+3  A: 

You should use template class forward declarations in either headers
template <class T> class X;
is perfectly good template class forward declaration.

ULysses
+3  A: 

What you need to do is seperate the class declerations from the implementation. So something like

template <class T> class Foo : public Base
{
    public:
    Base* convert(ID) const;
}

template <class T> class Bar : public Base
{
    public:
    Base* convert(ID) const;
}

template <class T> Base* Foo<T>::convert(ID) const {return new Bar<T>;}
template <class T> Base* Bar<T>::convert(ID) const {return new Foo<T>;}

This way, you have complete class definitions when the functions are defined.

KeithB
This sounds promising. I'm fiddling with it.
Jon Purdy
All set! Thanks very much.
Jon Purdy