Переопределить функцию-член с другим типом возврата

Рассмотрим пример ниже:

#include <iostream>

using namespace std;

class base
{
   public:
      virtual int func()
      {
         cout << "vfunc in base class\n";
         return 0;
      }
};

class derived: public base
{
   public:
      double func()
      {
         cout << "vfunc in derived class\n";
         return 0;
      }
};

int main()
{
   base *bptr = new derived;
   bptr->func();

   return 0;
}

Компилятор дает ошибку для вышеуказанного кода, что для функции overriden существует конфликтный тип. Почему невозможно переопределить функцию в производном классе с другим возвращаемым типом?

Я считаю, что для переопределения функции виртуальный метод базового класса должен быть переопределен в производном классе. Чтобы переопределить метод, подписи методов должны быть одинаковыми. Поскольку тип возврата не является частью подписи, я считаю, что даже если есть разница в типе возврата, метод все равно будет переопределен? В этом случае для кода выше виртуальная функция func переопределяется в производном классе с другим типом возвращаемого значения. Но компилятор выдает ошибку. Правильно ли я понимаю?

Ответы

Ответ 1

Переопределение по существу означает, что либо метод базового класса, либо метод класса Derived будут вызываться во время выполнения в зависимости от фактического объекта, на который указывает указатель.
Это означает, что:
i.e: Каждое место, где может быть вызван метод базового класса, может быть заменено вызовом метода Derived класса без изменения кода вызова.

Чтобы достичь этого, единственный возможный способ - ограничить возвращаемые типы переопределяющих виртуальных методов возвратом того же типа, что и базовый класс, или тип, полученный из этого (типы возвращаемых вариантов совместного использования), а стандарт применяет этот состояние.

Если вышеуказанное условие не было на месте, оно оставит окно, чтобы сломать существующий код, добавив новые функции.

Ответ 2

Чтобы переопределить виртуальную функцию, возвращаемое значение должно быть точно таким же *. С++ не будет автоматически конвертировать между double и int здесь - в конце концов, как он узнает, какой тип возврата вы хотите получить при вызове из указателя производного класса? Обратите внимание: если вы измените часть подписи (параметры, константа и т.д.), Вы также можете изменить возвращаемое значение.

* - строго говоря, он должен быть "ковариантным". Это означает, что возвращаемый вами тип должен быть подмножеством возвращаемого типа родительской функции. Например, если родительский класс возвращает a base *, вы можете вернуть derived *. Поскольку derived одновременно также base s, компилятор позволяет вам переопределить таким образом. Но вы не можете возвращать совершенно несвязанные типы, такие как int и double; просто потому, что неявное преобразование не означает, что компилятор позволит вам сделать это переопределение.

Ответ 3

См. этот вопрос. Подводя итог, вы можете только переопределить виртуальную функцию с помощью другого типа возврата, если типы covariant.

Ответ 4

Если вы хотите переопределить, попробуйте использовать шаблон.

См. следующее:

#include <iostream>

using namespace std;

class base
{
   public:
      template<typename X> X func()
      {
         cout << "vfunc in base class\n";
         return static_cast<X>(0);
      }  
};    

class derived: public base
{
   public:
      template<typename X> X func()
      {
         cout << "vfunc in derived class\n";
         return static_cast<X>(2);
      }  
};    

int main()
{
   derived *bptr = new derived;
   cout << bptr->func<int>() << endl;
   cout << dynamic_cast<base*>(bptr)->func<int>() << endl;

   derived *bptr2 = new derived;
   cout << bptr->func<double>() << endl;
   cout << dynamic_cast<base*>(bptr)->func<int>() << endl;


   return 0;
}

Конечно, вам не нужно объявлять это на двух разных классах таким образом:

class base
{
   public:
      int func()
      {
         cout << "vfunc in base class\n";
         return 0;
      }  

      double func(){
        cout << "vfunc for double class\n";
        return 2.;

      }
};

Ответ 5

Переопределение невозможно, так как подписи разные. Основной целью переопределения является полиморфизм, но это невозможно в приведенном выше примере