Возможно ли, чтобы унаследованный класс реализовал виртуальную функцию с другим типом возвращаемого значения (не используя шаблон в качестве возврата)?
Возвращаемый тип виртуальной функции С++
Ответ 1
В некоторых случаях, да, законным для производного класса является переопределение виртуальной функции с использованием другого типа возвращаемого значения, если тип возвращаемого значения является ковариантным с исходным типом возвращаемого значения. Например, рассмотрим следующее:
class Base {
public:
virtual ~Base() {}
virtual Base* clone() const = 0;
};
class Derived: public Base {
public:
virtual Derived* clone() const {
return new Derived(*this);
}
};
Здесь Base определяет чистую виртуальную функцию с именем clone, которая возвращает Base *. В производной реализации эта виртуальная функция переопределяется с использованием типа возврата Derived *. Хотя тип возврата не совпадает с базовым, это абсолютно безопасно, потому что в любое время вы пишете
Base* ptr = /* ... */
Base* clone = ptr->clone();
Вызов clone() всегда будет возвращать указатель на объект Base, так как даже если он возвращает Derived*, этот указатель неявно конвертируется в Base*, и операция корректно определена.
В общем случае возвращаемый тип функции никогда не считается частью его подписи. Вы можете переопределить функцию-член с любым типом возвращаемого значения, если тип возвращаемого значения является ковариантным.
Ответ 2
Да. Типы возвращаемых данных могут быть разными, если они covariant. Стандарт С++ описывает это следующим образом (§10.3/5):
Возвращаемый тип переопределяющей функции должен быть либо идентичен возвращаемому типу переопределенной функции, либо ковариант с классами функций. Если функция
D::fпереопределяет функциюB::f, возвращаемый тип функций ковариант, если они удовлетворяют следующим критериям:
- оба являются указателями на классы или ссылки на классы 98)
- класс возвращаемого типа
B::fявляется тем же классом, что и класс возвращаемого типаD::fили, является однозначным прямым или косвенным базовым классом класса в возвращаемом типеD::fи доступен вD- оба указателя или ссылки имеют одинаковую cv-квалификацию, а тип класса в возвращаемом типе
D::fимеет ту же самую cv-квалификацию, что или менее cv-qualification, чем тип класса в возвращаемом типеB::f.
Сноска 98 указывает, что "многоуровневые указатели на классы или ссылки на многоуровневые указатели на классы не допускаются".
Короче говоря, если D является подтипом B, то возвращаемый тип функции в D должен быть подтипом возвращаемого типа функции в B. Самый распространенный пример - когда типы возврата сами основаны на D и B, но они не обязательно должны быть. Рассмотрим это, где мы имеем две отдельные иерархии типов:
struct Base { /* ... */ };
struct Derived: public Base { /* ... */ };
struct B {
virtual Base* func() { return new Base; }
virtual ~B() { }
};
struct D: public B {
Derived* func() { return new Derived; }
};
int main() {
B* b = new D;
Base* base = b->func();
delete base;
delete b;
}
Причина этого в том, что любой вызывающий объект func ожидает указатель Base. Любой указатель Base будет делать. Итак, если D::func promises всегда возвращает указатель Derived, то он всегда будет удовлетворять контракту, установленному классом предка, потому что любой указатель Derived может быть неявно преобразован в указатель Base. Таким образом, абоненты всегда получат то, что они ожидают.
В дополнение к тому, что тип возвращаемого значения может меняться, некоторые языки также позволяют изменять типы параметров функции переопределения. Когда они это делают, они обычно должны быть контравариантными. То есть, если B::f принимает a Derived*, тогда D::f будет разрешено принимать Base*. Потомкам разрешено быть свободными в том, что они будут принимать, и более строгими в том, что они возвращают. С++ не допускает контравариантность параметрического типа. Если вы измените типы параметров, С++ считает это совершенно новой функцией, поэтому вы начинаете перегружаться и скрываться. Подробнее об этой теме см. Ковариация и контравариантность (информатика) в Википедии.
Ответ 3
Реализация виртуальной функции производного класса может иметь Ковариантный тип возврата.