2

I have a need in my code for two template classes to be composed of a member field of each other. For example, I have two files,

templates.h

template <class T> class B;

template <class T>
class A
{
    B<A> a;

    // fields and methods dependent on T
};

template <class T>
class B
{
    A<B> b;

    // fields and methods dependent on T
};

main.cpp

#include "templates.h"

int main()
{
    A<int> a;
}

When I compile I receive the output shown in this link

http://pastebin.com/taBWZjar

I am using the g++ compiler. When I type g++ --version, I get

g++ (Gentoo 4.7.2 p1.3, pie-0.5.5) 4.7.2

If this is not possible to do in c++, what is an alternative or work around? Or perhaps is this a bug with my compiler?

Michael Wojcik
  • 341
  • 4
  • 16
  • 1
    both of your class templates do not depend on T at all. then why do you need the class templates? – taocp Apr 25 '13 at 02:28
  • Its because the template classes are individually used elsewhere in my code too (not in the example though). Some other non-template classes use template class A for its unique service, or template class B for its unique service but both template classes use each other to accomplish their individual service. There are additional members that utilize T in both A and B. – Michael Wojcik Apr 25 '13 at 02:34
  • I touched up the example to clarify that. – Michael Wojcik Apr 25 '13 at 02:41
  • 3
    My head-wrapping around the infinite recursion in this is second only to my desire to know (a) why you would want to this, followed closely by (b) what problem are you *really* trying to solve. And its not a bug (in your compiler, anyway). The only reason this doesn't break-on-compile is because of the templates. Were these regular classes even with your forward-declare you'd still be bent. – WhozCraig Apr 25 '13 at 02:44

1 Answers1

7

Your current design contains a serious flaw which is known as circular dependency. It will never compile, and the best way to fix it is to redesign your class hierarchy.

To give you the clue why it can never compile, lets simplify the situation by removing templates:

class B;

class A {
  B b;
};

class B {
  A a;
};

Now try to think as a compiler. To know how much space is needed for allocation of class A (and compiler has to know that at compile-time), it needs to know how much space is needed for allocation of class B, and vice versa. Clearly, this is a circular dependency. You can try to compile it yourself and see that compiler complains about it.

One possible way to fix that would be switching to pointers (or references):

class B;

class A {
  B* b; // B& b; is possible too
};

class B {
  A a;
};

This should compile just fine. The reason is that now the compiler knows that b is a pointer, and the space needed to accommodate that pointer is fixed (4 bytes for 32-bit targets, and 8 bytes for 64-bit targets accordingly).

NOTE: Pointer/reference to a in B is unnecessary because A is already defined at that point, and therefore we can use it as a direct member in B.

In your situation you complicated things even further with templates, and while you could utilize the proposed fix (using pointers/references) and combine it with templates, I don't recommend it. Your code has to be totally redesigned.

Alexander Shukaev
  • 16,674
  • 8
  • 70
  • 85
  • Thanks for the detailed reply. It seems like it would be impossible to make the above work with templates because the templates have to be both defined and declared in a header file. I'll start redesigning my code like you stated though. – Michael Wojcik Apr 25 '13 at 17:45
  • 2
    "the best way to fix it is to redesign your class hierarchy". How should it be designed? – birgersp Oct 24 '14 at 08:48