Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 570
Tobias Avatar asked Aug 11 '09 09:08

Tobias


People also ask

Can you forward declare a function?

To write a forward declaration for a function, we use a declaration statement called a function prototype. The function prototype consists of the function header (the function's return type, name, and parameter types), terminated with a semicolon. The function body is not included in the prototype.

Can you forward declare in C?

In Objective-C, classes and protocols can be forward-declared if you only need to use them as part of an object pointer type, e.g. MyClass * or id<MyProtocol>.

How do you forward a variable declaration in C++?

In C++, a variable declaration must be prefixed with extern : extern A Unit[10]; // ... A Unit[10] = { ... }; (Note that in C++ you can omit the leading struct .)

Why Forward declare instead of include?

A forward declaration is much faster to parse than a whole header file that itself may include even more header files. Also, if you change something in the header file for class B, everything including that header will have to be recompiled.


2 Answers

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()); }
like image 85
AProgrammer Avatar answered Nov 10 '22 00:11

AProgrammer


Doesn't static polymorphism solve your problem? Feeding the base class with the derived class through template argument? So the base class will know the derivative Type and declare a proper virtual?

like image 32
thAAAnos Avatar answered Nov 10 '22 02:11

thAAAnos