3
votes

I came across a circular dependency issue, where the order of inclusion of headers matters. This problem is similar, but it does not document the solution where one class instantiates an object of the other.

Problem:

  • I have two classes: Foo and Bar. Bar is templatized. (Foo may or may not be templatized).
  • Foo has an instance of Bar, and Bar has a pointer of type Foo, and the implementation of Bar needs to access members of Foo through this pointer.
  • Foo and Bar have to be implemented within separate files. As Bar is templatized, its declaration and implementation have to be in the same file.

In the following code, if main.cpp includes Bar.h before Foo.h the code compiles, and does not when Foo.h is included before Bar.h. Is there a way to make the code compile, irrespective of the order in which headers are included in main.cpp?

Foo.h

#ifndef FOO_H
#define FOO_H

#include"Bar.h"
class Foo
{
    public:
        Bar<> B;
        void fooStuff(){};

};
#endif

Bar.h

#ifndef BAR_H
#define BAR_H

class Foo;
template<int N=0> class Bar
{
    public:
        Foo * F;
        void barStuff();
};

#include"Foo.h"
 template<int N> void Bar<N>::barStuff()
{
        F->fooStuff();
};
#endif

main.cpp

#include"Foo.h"
#include"Bar.h"
int main()
{
    Foo F;
    F.B.barStuff();
};
2

2 Answers

4
votes

Yes: declare Foo before Bar, since Bar only uses a pointer, which doesn't need a full definition. Then define Foo after Bar - it uses an object, and so it does need the definition.

class Foo;

template<int N> class Bar
{
    public:
        Foo * M;
        void barStuff();
};

class Foo
{
    public:
        Bar<42> B;
        void fooStuff(){}
};

template<int N> void Bar<N>::barStuff()
{
    M->fooStuff();
}

In general, you need the full definition for anything that needs any properties of the class, such as its size or members, and only a declaration to use the class name when declaring functions, pointers or references.

2
votes

I need Bar to be included before Foo, no matter in what order they're included in main.cpp. The following hack seems to work:

In Foo.h, include Bar.h outside the header guards. As Bar has header guards too, it does not get included multiple times.

Foo.h

#include"Bar.h"

#ifndef FOO_H
#define FOO_H


class Foo
{
    public:
        Bar<> B;
        void fooStuff(){};

};
#endif

Bar.h

#ifndef BAR_H
#define BAR_H

class Foo;
template<int N=0> class Bar
{
    public:
        Foo * F;
        void barStuff();
};

#include"Foo.h"
 template<int N> void Bar<N>::barStuff()
{
        F->fooStuff();
};
#endif

main.cpp

#include"Foo.h"
#include"Bar.h"
int main()
{
    Foo F;
    F.B.barStuff();
};