Are covariant cyclic dependancies legal?

  • Thread starter Thread starter Jon
  • Start date Start date
J

Jon

struct B2;

struct B1 {
virtual B2* get2() = 0;
};

struct B2 {
virtual B1* get1() = 0;
};

struct D2; //"struct D2 : public B2;" is illegal

struct D1 : public B1 {
virtual D2* get2(); //Can I declare this somehow?
};

struct D2 : public B2 {
virtual D1* get1();
};
 
Jon said:
struct B2;

struct B1 {
virtual B2* get2() = 0;
};

struct B2 {
virtual B1* get1() = 0;
};

struct D2; //"struct D2 : public B2;" is illegal

struct D1 : public B1 {
virtual D2* get2(); //Can I declare this somehow?
};

struct D2 : public B2 {
virtual D1* get1();
};

I think it's impossible. You'll have to make either or both
non-covariant. Covariance generally only saves you the odd static_cast
in any case.

Tom
 
struct B2;
struct B1 {
virtual B2* get2() = 0;
};

struct B2 {
virtual B1* get1() = 0;
};

struct D2; //"struct D2 : public B2;" is illegal

struct D1 : public B1 {
virtual D2* get2(); //Can I declare this somehow?
};

struct D2 : public B2 {
virtual D1* get1();
};

I think it's not possible as compiler does not know from the forward
declaration that D2 inherits B2.
 
Jon said:
struct B2;

struct B1 {
virtual B2* get2() = 0;
};

struct B2 {
virtual B1* get1() = 0;
};

struct D2; //"struct D2 : public B2;" is illegal

struct D1 : public B1 {
virtual D2* get2(); //Can I declare this somehow?
};

struct D2 : public B2 {
virtual D1* get1();
};

Very good question.

To the best of my knowledge, the only way to get this to work is to make
B1 a nested class within B2 (or vice versa):

struct B2;

struct B1 {
virtual B2* get2() = 0;
};

struct B2 {
virtual B1* get1() = 0;
};

struct D2; //"struct D2 : public B2;" is illegal

struct D2 : public B2 {

struct D1 : public B1 {
virtual D2* get2(); //Can I declare this somehow?
};

virtual D1* get1();
};

typedef D2::D1 D1;

You have to ask yourself the question if it's worth it, though. It's
quite a bit messy, in my humble opinion. The language purists are going
to raise an eyebrow. It might be worth considering, but you have to
think about maintenance issues in the long run. At least you should very
carefully document it. One will have a hard time understanding the
reason why D1 was embedded inside D2. It doesn't belong there. You can't
extend such a hierarchy easily -- it's quite a big restriction that a
future class D3, for example, can't be implemented in a separate file,
you have to edit the definition of D2 for this. Unless your hierarchy is
already very stable, I would forget about this.

Here's a much cleaner solution:

struct B2;

struct B1 {
virtual B2* get2() = 0;
};

struct B2 {
virtual B1* get1() = 0;
};

struct D2; //"struct D2 : public B2;" is illegal

struct D1 : public B1 {
virtual B2* get2(); //Can I declare this somehow?
inline D2* get2_D();
};

struct D2 : public B2 {
virtual B1* get1();
D1* get1_D() { return static_cast<D1*>(get1()); }
};

D2* D1::get2_D() { return static_cast<D2*>(get2()); }

This might be just slightly less invonvenient to use, but the solution
is more robust, and the API is not subject to any restriction.

This is how things are done when you can't have covariant return types.
Your code is a good example. Another good case is boost::shared_ptr<D1>,
boost::shared_ptr<D2>. There, again, you can't use covariant return
types at all.

There was life before covariant return types were introduced to the
language, and back then people still survived somehow. ;-)

Tom
 
Back
Top