Есть ли способ форвард объявить ковариацию? - PullRequest
15 голосов
/ 11 августа 2009

Предположим, у меня есть эти абстрактные классы Foo и Bar:

class Foo;
class Bar;

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

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

Предположим, что у меня есть производные классы ConcreteFoo и ConcreteBar. Я хочу ковариантно уточнить тип возвращаемого значения для методов foo() и bar() следующим образом:

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

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

Это не скомпилируется, поскольку наш любимый однопроходный компилятор не знает, что ConcreteBar будет наследоваться от Bar, и поэтому ConcreteBar является совершенно допустимым ковариантным типом возврата. Простое объявление ConcreteBar также не работает, поскольку ничего не говорит компилятору о наследовании.

Является ли это недостатком C ++, с которым мне придется смириться, или есть ли способ обойти эту дилемму?

Ответы [ 4 ]

4 голосов
/ 11 августа 2009

Вы можете подделать его довольно легко, но вы потеряете статическую проверку типов. Если вы замените dynamic_casts на static_casts, у вас есть то, что компилятор использует внутри, но у вас нет проверки динамического или статического типа:

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()); }
3 голосов
/ 14 февраля 2011

Разве статический полиморфизм не решает вашу проблему? Подача базового класса производным классом через аргумент шаблона? Таким образом, базовый класс будет знать производный тип и объявит правильный виртуальный?

2 голосов
/ 11 августа 2009

Ковариация основана на диаграмме наследования, так как вы не можете объявить

class ConcreteBar : public Bar;

следовательно, нет никакого способа сообщить компилятору о ковариации.

Но вы можете сделать это с помощью шаблонов, объявив ConcretFoo :: bar в качестве шаблона, и последующее ограничение позволит вам решить эту проблему

1 голос
/ 31 августа 2017

Как насчет этого?

template <class BarType>
class Foo
{
public:
    virtual BarType* bar() = 0;
};

template <class FooType>
class Bar
{
public:
    virtual FooType* foo() = 0;
};

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

class ConcreteBar : public Bar<ConcreteFoo>
{
public:
    ConcreteFoo* foo();
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...