Search code examples
c++covarianceforward-declaration

Is there a way to forward declare covariance?


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 a shortcoming of C++ I'll have to live with or is there actually a way around this dilemma?


Solution

  • You can fake it quite easily, but you lose 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()); }