5

Is there a way to define circular references without using pointers?

I need to have somthing like this:

struct A;
struct B {
    A a;
};

struct A {
    B b;
};

Thanks!

Michel
  • 153
  • 1
  • 2
  • 7
  • 1
    Note: It seems fishy to me to forward-declare `A` to be a `class` and later define it as a `struct`. – sbi Aug 25 '09 at 17:34
  • The above cannot possibly compile - and doesn't under vs2008 - because A is not defined when struct B is being defined. (btw, yes your forward declarations: struct/class should match the definitions) – quamrana Aug 25 '09 at 18:59

5 Answers5

15

You can use references instead

struct A;
struct B {
  A& a;
};

struct A {
  B b;
};

But no it's not possible to create a circular reference without some level of indirection. What your sample is doing is not even creating a circular reference, it's attempting to create a recursive definition. The result would be a structure of infinite size and hence not legal.

JaredPar
  • 703,665
  • 143
  • 1,211
  • 1,438
15

No, there's not. Such structure would have infinite size.

You can use smart pointers (shared_ptr and weak_ptr) to avoid direct pointer manipulation, but that's about it.

Filip Navara
  • 4,768
  • 1
  • 24
  • 36
7

How could this work? If I remember correctly, the address value of a reference can't be modified once set, so you can't define a circular reference.

It could work like the following (same as Jared's example plus constructors defined):

struct A;

struct B {
  A& m_a;
  B(A& a) : m_a(a) {}
};

struct A {
  B m_b;
  //construct B m_b member using a reference to self
  A() : m_b(*this) {}
  //construct B m_b member using a reference to other
  A(A& other) : m_b(other) {}
};
ChrisW
  • 53,487
  • 12
  • 110
  • 213
1

In C++, T o means "an object of type T, not a reference to some T (as, for example, with reference types in C# and Java). With the code from your question, type A would have a sub object of type B (named b), and that B in turn would have a sub object of type A (named a). Now, that a would in turn have another A inside (again called a), which then has another B, which...

No, this will not work.

What you probably want is that an A referres to a B, which in turn referres that A. This can be done using pointers:

struct A;
struct B {
    A* a;
    B(A*);
};

struct A {
    B* b;
    A(B* b_) : b(b_)  { if(b) b.a = this; }
};

B::B(A* a_) : : a(a_) { if(a) a.b = this; }

I don't think it can be done using references.

sbi
  • 212,637
  • 45
  • 247
  • 432
0

ChrisW's solution can be generalized a bit like this :

template <class defaultState> struct Context;

struct State1 {
    Context<State1>& mContext;
    State1(Context<State1> & ref) : mContext(ref) {}
};

template <class TDefaultState>
struct Context {
    TDefaultState mState;
    Context() : mState(*this) {}
};

This now allows you to do

Context<State1> demo;

Further, State can have some template helper code as well

template <class State>
struct TState {
    typedef Context<State> TContext;
    typedef TState<State> TBase;
    Context<State> & mContext;
    TState(Context<State> &ref) : mContext(ref) {}
};

struct State2 : TState<State2> {
    State2(TContext & ref) : TBase(ref) {}
};
struct State3 : TState<State3> {
    State3(TContext & ref) : TBase(ref) {}
};

Which now allows you to do any of

Context<State2> demo2;
Context<State3> demo3;
kert
  • 2,061
  • 20
  • 22