Тип возвращаемой виртуальной функции C++

Возможно ли для унаследованного класса реализовать виртуальную функцию с другим типом возврата (не используя шаблон в качестве возврата)?

3 ответа

Решение

В некоторых случаях, да, для производного класса допустимо переопределять виртуальную функцию с использованием другого возвращаемого типа, если возвращаемый тип является ковариантным по отношению к исходному возвращаемому типу. Например, рассмотрим следующее:

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* и операция четко определена.

В более общем смысле тип возвращаемого значения функции никогда не считается частью ее сигнатуры. Вы можете переопределить функцию-член любым типом возвращаемого значения, если тип возвращаемого значения является ковариантным.

Да. Типы возвращаемых данных могут быть разными, если они ковариантны. Стандарт C++ описывает это так (§10.3/5):

Тип возврата переопределяемой функции должен быть либо идентичным типу возврата переопределенной функции, либо ковариантно классам функций. Если функция D::f переопределяет функцию B::fтип возвращаемых функций ковариантен, если удовлетворяет следующим критериям:

  • оба указатели на классы или ссылки на классы98)
  • класс в возвращаемом типе B::f тот же класс, что и класс в типе возвращаемого значения D::f или, является однозначным прямым или косвенным базовым классом класса в типе возврата D::f и доступен в D
  • оба указателя или ссылки имеют одинаковую квалификацию cv и тип класса в возвращаемом типе D::f имеет ту же квалификацию cv, что и квалификация cv или меньше, чем тип класса в возвращаемом типе 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 обещает всегда возвращать Derived указатель, то он всегда будет удовлетворять контракту, установленному классом предка, потому что любой Derived указатель может быть неявно преобразован в Base указатель. Таким образом, абоненты всегда получат то, что ожидают.


Помимо возможности изменения типа возвращаемого значения, некоторые языки также допускают изменение типов параметров переопределяющей функции. Когда они делают это, они обычно должны быть противоположными. То есть если B::f принимает Derived*, затем D::f будет разрешено принять Base*, Потомкам разрешено быть более свободными в том, что они примут, и строже в том, что они возвращают. C++ не допускает контравариантности типов параметров. Если вы меняете типы параметров, C++ считает это совершенно новой функцией, поэтому вы начинаете перегружаться и прятаться. Для получения дополнительной информации по этой теме см. Ковариация и контравариантность (информатика) в Википедии.

Реализация производного класса виртуальной функции может иметь Covariant Return Type.

Другие вопросы по тегам