views:

130

answers:

3

Hello, this code:

template <class tB>
struct A{
  typedef float atype;
  typedef typename tB::btype typeB;
};
template <class tA>
struct B{
  typedef float btype;
  typedef typename tA::atype typeA;
};

struct MyB;
struct MyA: public A<MyB>{};
struct MyB: public B<MyA>{};

int main(int argc, char *argv[])
{
}

does not compile because "main.cpp:6: error: invalid use of incomplete type ‘struct MyB’".

Basically the compiler cannot solve the loop because definition of A depends on definition of B an viceversa. Is there a way to sort this out? thanks,

+1  A: 

I think that since you are typedef'ing tB::btype the compiler would need to know the type struct MyB, which it doesn't (you only forward declared it). You can check this: in fact if you comment out the reference to tA::atype in struct B you still get the same error, while if you comment out the typedef typeB in struct A the compiler doesn't complain.

Putting that aside, are you sure that you need such a circular dependence?

Francesco
+3  A: 

This cannot be resolved directly. There was a very similar question (although not involving templates) posted yesterday: http://stackoverflow.com/questions/2410532/c-how-can-i-avoid-invalid-covariant-return-type-in-inherited-classes-without

Your two options are to redesign your solutions so that this cross-dependency is no longer required, or to redesign your solution so that you are only using the template parameters to declare references and pointers (because their declarations do not require that a complete definition of the type is available at the time).

Tyler McHenry
Circular dependencies, whenever they creep in, are usually a sign of smelly design. Rethink the design and either decouple the two or just fuse them.
Matthieu M.
+1  A: 

If you get rid of the template smoke screen, what you have here involves infinite recursion. For the moment, think of inheritance as a slightly different way of stating containment (which it is). You're saying a myA contains a myB, which in turn contains a myA, which in turn contains a myB, and so on forever -- i.e. any single object of either type has infinite size...

Edit: as litb pointed out, neither of these structs contains anything, so in theory, they don't occupy any space (thanks to the empty base class optimization). While it's true that this can prevent the structs themselves from having infinite size, the AST the compiler would have to generate for either is still infinite -- e.g., myA::myB::myA::myB::myA::myB::myA::myB ...myB::btype (a final item of myA::atype, myA::tA, or myB::tB)is a valid type name for any level of nesting. At least as a compiler is normally written, no cycles are allowed in the AST, leaving an infinite AST as the only alternative.

Jerry Coffin
While i myself got it wrong before (this is some hairy relationship i think), i cannot see infinite sizes here. `myB` contains a `float` typedef and `myA` contains a `float` typedef. They contain no data of each other.
Johannes Schaub - litb