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