c++ooppolymorphismcovariant-return-types

What is the point of a covariant return type in a virtual function?


The most common usage of a covariant return type I've seen is via virtual functions. The simplest example I can come up with looks something like this:

class Base {
public:
  virtual Base& get() { return *this; }
};


class Derived : public Base {
public:
  Derived& get() override { return *this; }
};

One thing I cannot really comprehend is why make these functions virtual. The return type of the get function is defined statically at the call point, and there is no way we can get Derived& by calling Base::get even if *this can be downcast to Derived. This all means polymorphism won't work for return types anyway. To me it looks a bit more reasonable to make these functions non-virtual and just let the base method get redefined by a child method. But still people prefer making them virtual, at least this is what I've noticed. Is there an explanation for this?


Solution

  • Using more useful example clone:

    class Base {
    public:
      virtual ~Base() = default;
      virtual Base* clone() const { return new Base(*this); }
      virtual void print() const { std::cout << "Base\n"; }
    };
    
    
    class Derived : public Base {
    public:
      Derived* clone()  const override { return new Derived(*this); }
      void print() const override { std::cout << "Derived\n"; }
      void foo();
    };
    

    Now, with

    void clone_and_print(const Base& base)
    {
        auto p = base.clone(); // auto is Base*, dynamic type is the same as the one of base
        p->print(); // print Base or Derived
        delete p;
    }
    

    all is good. removing virtual as you suggest would change the behavior, and print Base unconditionally.

    Now, a case when covariant return type is useful:

    void clone_and_foo(const Derived& derived)
    {
        auto p = derived.clone(); // auto is Derived*, not Base*
        p->foo(); // Won't work without covariant return type. would require a cast
        delete p;
    }