Есть ли способ переслать декларацию ковариации?
Предположим, что у меня есть эти абстрактные классы 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
тоже не работает, поскольку оно не сообщает компилятору ничего о наследовании.
Является ли это недостатком С++, с которым мне придется жить, или есть ли способ преодолеть эту дилемму?
Ответы
Ответ 1
Вы можете легко подделать его, но вы потеряете проверку статического типа. Если вы замените 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()); }
Ответ 2
Не статический полиморфизм решает вашу проблему?
Подача базового класса с производным классом через шаблонный аргумент?
Таким образом, базовый класс будет знать производный тип и объявить правильный виртуальный?
Ответ 3
Ковариация основана на диаграмме наследования, так что вы не можете объявить
class ConcreteBar : public Bar;
следовательно, не имеет возможности рассказать компилятору о ковариации.
Но вы можете сделать это с помощью шаблонов, объявить ConcretFoo:: bar в качестве шаблона, а затем ограничить это решение.
Ответ 4
Как насчет этого.
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();
};