Search code examples
c++overridingvirtual

C++ override understanding with different method's signatures


I expects that in c++ you can override method if it has same signature in Base and Derived classes including name, arguments and return type.

If I try compile something like this I'll failure on both methods (get, get2) expectedly:

#include <iostream>

class A
{
public:
  A() {}
  virtual int get() { return 0; }
  virtual int *get2() { return 0; }
};

class B : public A
{
public:
  B() {}
  float get() override { return 0.f; }
  float *get2() override { return 0.f; }
};

using namespace std;

int main()
{
  A a;
  B b;
  cout << a.get() << endl;
  cout << b.get() << endl;

  return 0;
}

I have code:

#include <iostream>
class C
{
public:
  C(){};
  virtual int get() { return 0; };
};

class D : public C
{
public:
  D(){};
  int get() override { return 1; };
};

class E : public D
{
public:
  E(){};
  virtual int get() { return 2; };
};

class A
{
public:
  A() { d = new D(); }
  virtual D *get() { return dynamic_cast< D * >(d); }
  D *d;
};

class B : public A
{
public:
  B() { d = new E(); }
  E *get() override { return dynamic_cast< E * >(d); }
};

class Z : public A
{
public:
  Z() { d = new E(); }
  C *get() override { return dynamic_cast< C * >(d); }
};

using namespace std;

int main()
{
  A a;
  B b;
  Z z;
  cout << a.get()->get() << endl;
  cout << b.get()->get() << endl;
  cout << z.get()->get() << endl;

  return 0;
}

And I expect failure on both classes (B and Z). But I got failure only on class Z. Can someone explain this?


Solution

  • If a base class declares a virtual function that returns a pointer or reference to a class, then overriding virtual functions can return a pointer or reference to a class type that derives from the base function's declared return type.
    This is known as "covariant return type".

    Example:

    class Object {};
    class Derived : public Object {};
    
    class Interface {
    public:
        virtual Object* get() = 0;
    };
    
    class Impl : public Interface {
    public:
        Derived* get() override { return nullptr; } // OK
    };
    

    If you have a pointer to Interface and call get(), you get back an Object*, since Derived* is convertible to Object* due to inheritance.

    However, if you have an instance of Impl, and you call get() directly on it, you can benefit from the more specific return type. (After all, if you're working with the impl type rather than its interface, it's not a problem to know more about the implementation.)

    According to the standard:

    [class.virtual] 11.6.2.8: The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. If a function D::f overrides a function B::f, the return types of the functions are covariant if they satisfy the following criteria:

    • both are pointers to classes, both are lvalue references to classes, or both are rvalue references to classes
    • the class in the return type of B::f is the same class as the class in the return type of D::f, or is an unambiguous and accessible direct or indirect base class of the class in the return type of D::f
    • both pointers or references have the same cv-qualification and the class type in the return type of D::f has the same cv-qualification as or less cv-qualification than the class type in the return type of B::f.