views:

60

answers:

2

I have a situation in which A has a reference to a class C defined inside B, and C has an instance of class B.

When I try to compile the code below, I get "field a has incomplete type". I assume this is because the compiler does not know how much memory it should allocate for an instance of A.

class A;

class B {
public:
  class C {
    A a;
  };
};

class A {
  A(const B::C& _c) 
    : c(_c)
  {}
  const B::C& c;
};

But when I try to compile this, I get "C in class B does not name a type":

class B;
class B::C;

class A {
  A(const B::C& _c) 
    : c(_c)
  {}
  const B::C& c;
};


class B {      
public:
  class C {
    A a;
  };
};

How can I convince the compiler that B::C is a real type?

+1  A: 

The forward declaration for A doesn't serve a purpose: you can't declare an instance of an incomplete type.

As to B::C, I don't think you can use nested names in an incomplete type. Just don't nest C in B: as far as I know this doesn't give you any significant advantages* and stops you from forward declaring it.

*The only advantage I can think of is that you can define it in the private section, but then A would have no business with it in the first place.

UncleBens
I can do that if I have to, but in the code I'm writing it makes more sense to have C be nested in B. Is there a good reason C++ doesn't allow me to declare nested incomplete types? Or is this just a quirk of the language?
Andrew Cone
@Andrew: It appears that you can do this (see j_random_hacker's answer). Whether the complication is worth it... I guess another reason to nest types is to get class names decorated - however, a namespace is a dedicated tool for that.
UncleBens
+4  A: 

As an absolute guess, I notice there's one permutation you haven't tried:

class B {
public:
  class C; // Forward declaration
};

class A {
  A(const B::C& _c) 
    : c(_c)
  {}
  const B::C& c;
};

class B::C {
  A a;
  C() : a(*this) {}    // Thanks Nim for pointing this out!
};

This is quite possibly illegal, but worth a shot I think. If it doesn't work, then I don't see any way around the problem.

j_random_hacker
Seems to be legal.
UncleBens
I don't believe this will compile, the default constructor for A doesn't initialize the reference to C. However, to get the above to work, all you need is the constructor for C to initialize A with itself - but you have two different instances of A, perhaps that is that is what you required? (@j_random_hacker, apologies if you implied this in your answer!)
Nim
@Nim: Good point. Has anyone tried compiling this? I (lazily) haven't...
j_random_hacker
@Nim: Thinking about it, you're right. Updated. (Though technically the problem is that `A` does not have a default ctor, because a user-defined ctor was given -- not that it has a faulty default ctor that fails to initialise `c`.)
j_random_hacker
Nim
Nim
@Nim: Yes, that would be another way to solve it. Though I personally prefer to just avoid having a default ctor if reference members exist -- that way you know that it's always initialised. If I want something similar that can be initialised or not, I'll use a pointer.
j_random_hacker