A virtual function\'s return type should be the same type that is in base class, or covariant. But why do we have this restriction?
Because of the nonsense that would ensue:
struct foo
{
virtual int get() const { return 0; }
};
struct bar : foo
{
std::string get() const { return "this certainly isn't an int"; }
};
int main()
{
bar b;
foo* f = &b;
int result = f->get(); // int, right? ...right?
}
It isn't sensible to have a derived class return something completely unrelated.
Because how would the code that's using the return value cope with all sorts of unrelated types coming back? e.g.:
class A
{
public:
virtual float func();
};
class B: public A
{
public:
virtual char *func();
};
A *p = (some_condition) ? new A() : new B();
p->func(); // Oh no! What is the type?
The answer is very similar to the one given for "Why can't I assign a vector<Apple*> to a vector<Fruit*>?" on Bjarne Stroustrup's FAQ.
The ability to modify the return type would lead to a hole in the type safety of the language (see the answer from @GManNickG for a specific example) when dealing with polymorphic types.
There is one fairly common situation when affecting the return type would be ideal: when returning a polymorphic pointer from a virtual method of a base type. For example,
class Base {
public:
virtual Base* parent() = 0;
};
class Child : public Base {
public:
Base* parent() override
{
return parent_;
}
private:
Parent* parent_; // Assume `Parent` type exists.
};
Here we lost the type information that Child
knows about it's parent_
member. This leads to lots of casting, even though the type was at one point well defined. We can solve this using the Curiously Recurring Template Parameter (CRTP) idiom,
template<class ParentType>
class Base {
public:
virtual ParentType* parent()
{
return parent_;
}
private:
ParentType* parent_;
};
class Child : public Base<Parent> {
};
According to C++ Standard:
The return type of an overriding function shall be either identical to the return type of the overridden func- tion 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:
1) both are pointers to classes or references to classes
2) 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
3) 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.