views:

313

answers:

2

Suppose I have these abstract classes Foo and Bar:

class Foo;
class Bar;

class Foo
{
public:
  virtual Bar* bar() = 0;
};

class Bar
{
public:
  virtual Foo* foo() = 0;
};

Suppose further that I have the derived class ConcreteFoo and ConcreteBar. I want to covariantly refine the return type of the foo() and bar() methods like this:

class ConcreteFoo : public Foo
{
public:
  ConcreteBar* bar();
};

class ConcreteBar : public Bar
{
public:
  ConcreteFoo* foo();
};

This won't compile since our beloved single pass compiler does not know that ConcreteBar will inherit from Bar, and so that ConcreteBar is a perfectly legal covariant return type. Plain forward declaring ConcreteBar does not work, either, since it does not tell the compiler anything about inheritance.

Is this one of the many shortcomings of C++ I'll have to live with or is there actually a way around this dilemma?

+3  A: 

You can fake it quite easily, but you loose the static type checking. If you replace the dynamic_casts by static_casts, you have what the compiler is using internally, but you have no dynamic nor static type check:

class Foo;
class Bar;

class Foo
{
public:
  Bar* bar();
protected:
  virtual Bar* doBar();
};

class Bar;
{
public:
  Foo* foo();
public:
  virtual Foo* doFoo();
};

inline Bar* Foo::bar() { return doBar(); }
inline Foo* Bar::foo() { return doFoo(); }

class ConcreteFoo;
class ConcreteBar;
class ConcreteFoo : public Foo
{
public:
  ConcreteBar* bar();
protected:
  Bar* doBar();
};

class ConcreteBar : public Bar
{
public:
   ConcreteFoo* foo();
public:
   Foo* doFoo();
};

inline ConcreteBar* ConcreteFoo::bar() { return &dynamic_cast<ConcreteBar&>(*doBar()); }
inline ConcreteFoo* ConcreteBar::foo() { return &dynamic_cast<ConcreteFoo&>(*doFoo()); }
AProgrammer
+1 for the code effort ;)
neuro
This solution works, but surely won't win a beauty contest :)
Tobias
If the participants are restricted to those solving the stated problem, I'm not so sure of the outcome :-) You obviously can use the language provided covariance for one of the class, but I preferred to keep the symmetry.
AProgrammer
@Tobias: I don't think that the original design with the complex dependencies would win a beauty contest either. :P
David Rodríguez - dribeas
+3  A: 

Covariance is based on inheritance diagram, so since you cannot declare

class ConcreteBar : public Bar;

hence no way to tell compiler about covariance.

But you can do it with help of templates, declare ConcretFoo::bar as template and later bounding allows you solve this problem

Dewfy
I know that you cannot forward declare inheritance. And templates won't help, either, since a template member function cannot be virtual.
Tobias
Not exactly: here my sample that works!<pre><code>class ConcreteFoo : public Foo{public: template <class T> T* bar();};template <>ConcreteBar* ConcreteFoo::bar<ConcreteBar>(){}</code></pre>
Dewfy
Have you tried calling your method?
Tobias
yes, can send a workable and compilable sample (g++, VC++)
Dewfy
Dewfy: Please include a working sample in your answer, I cannot construct one from your given code. http://codepad.org/xl4NTdQt
Roger Pate
@Roger Pate done, look at http://codepad.org/xl4NTdQt#comment-ayWjjk4p sample works on msvc, g++
Dewfy
Ah, thanks, that's making ConcreteFoo a template rather than ConcreteFoo::bar as you first said.
Roger Pate