Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ virtual function return type

Is it possible for an inherited class to implement a virtual function with a different return type (not using a template as return)?

like image 559
Bob Avatar asked Jan 12 '11 03:01

Bob


People also ask

Does virtual function have return type?

The return type of an overriding virtual function may differ from the return type of the overridden virtual function. This overriding function would then be called a covariant virtual function. Suppose that B::f overrides the virtual function A::f .

Does pure virtual function return anything?

No. A function is pure virtual if and only if it is declared with = 0 .

What is C virtual function?

A virtual function is a member function that you expect to be redefined in derived classes. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.

Can virtual method have body?

Pure virtual functions (when we set = 0 ) can also have a function body.


2 Answers

In some cases, yes, it is legal for a derived class to override a virtual function using a different return type as long as the return type is covariant with the original return type. For example, consider the following:

class Base { public:     virtual ~Base() {}      virtual Base* clone() const = 0; };  class Derived: public Base { public:     virtual Derived* clone() const {         return new Derived(*this);     } }; 

Here, Base defines a pure virtual function called clone that returns a Base *. In the derived implementation, this virtual function is overridden using a return type of Derived *. Although the return type is not the same as in the base, this is perfectly safe because any time you would write

Base* ptr = /* ... */ Base* clone = ptr->clone(); 

The call to clone() will always return a pointer to a Base object, since even if it returns a Derived*, this pointer is implicitly convertible to a Base* and the operation is well-defined.

More generally, a function's return type is never considered part of its signature. You can override a member function with any return type as long as the return type is covariant.

like image 157
templatetypedef Avatar answered Oct 13 '22 04:10

templatetypedef


Yes. The return types are allowed to be different as long as they are covariant. The C++ standard describes it like this (§10.3/5):

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 type of the functions are covariant if the satisfy the following criteria:

  • both are pointers to classes or references to classes98)
  • 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 direct or indirect base class of the class in the return type of D::f and is accessible in D
  • 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.

Footnote 98 points out that "multi-level pointers to classes or references to multi-level pointers to classes are not allowed."

In short, if D is a subtype of B, then the return type of the function in D needs to be a subtype of the return type of the function in B. The most common example is when the return types are themselves based on D and B, but they don't have to be. Consider this, where we two separate type hierarchies:

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; } 

The reason this works is because any caller of func is expecting a Base pointer. Any Base pointer will do. So, if D::func promises to always return a Derived pointer, then it will always satisfy the contract laid out by the ancestor class because any Derived pointer can be implicitly converted to a Base pointer. Thus, callers will always get what they expect.


In addition to allowing the return type to vary, some languages allow the parameter types of the overriding function to vary, too. When they do that, they usually need to be contravariant. That is, if B::f accepts a Derived*, then D::f would be allowed to accept a Base*. Descendants are allowed to be looser in what they'll accept, and stricter in what they return. C++ does not allow parameter-type contravariance. If you change the parameter types, C++ considers it a brand new function, so you start getting into overloading and hiding. For more on this topic, see Covariance and contravariance (computer science) in Wikipedia.

like image 38
Rob Kennedy Avatar answered Oct 13 '22 04:10

Rob Kennedy