views:

140

answers:

1

Is there any way to declare these classes in a header file without indirection?

// Forwards declaration of B
class B;

class A
{
public:
    // Default parameter referring to B.  May return its parameter
    const B& func(const B& b = B());
};

class B
{
public:
    // B ctors
    B() {}
    B(const B&) {}

    // B has A as a member
    A a;
};

Visual C++ 2008 tells me with this:

error C2514: 'B' : class has no constructors

and points to the forward declaration of B ("class B;") and obviously can't see B's constructors below. A can't follow B because B contains A as a member.

If indirection must be used, what's the best way? Perhaps in C++0x B's A could be a unique_ptr member? Or maybe there's a boost class purely to sidestep this issue?

+7  A: 

Instead of a default parameter declare two overloads, one that takes a B by reference and one that takes no parameter. In the one that takes no parameter, call the other with B(), which will work because that method can be defined after B is defined.

...
    void func();
    void func(const B& b);
};

class B...

void A::func() { func(B()); }
void A::func(const B&) { }

Update:

func() returns a const B&...

That's probably not a good idea. With that definition, something like:

const B& foo = a.func();
foo.bar();

would cause the dreaded "undefined behavior" (i.e., crash) because the B to which you have a reference will be destroyed as soon as the first statement is complete. Returning things other than class members by reference is usually a bad idea.

If you really want to do this, then I think you're stuck with forcing the caller to explicitly pass in B(), that is have no default parameter.

a.func(B()).bar();

(This is the only way to avoid undefined behavior with such a function.)

Of course you could just return a copy instead of a reference, but I presume you have a reason for not doing that.

Depending on what you're doing you may be able to set up better semantics using a smart pointer like shared_ptr instead of references so that you can effectively ignore the lifetimes of the objects. You then have to start being careful of reference cycles instead, however.

I can't tell what you're trying to use this for, but you might want to have a look at some Design Patterns to see if there is an established best-practice for it. You may find that this little problem is a symptom of an unfortunate choice of class structure or containment.

Tim Sylvester
AshleysBrain