15

I have this piece of code (contrived from my real-life trouble)

It cannot compile, complaining ExtendsB does not implement B::Run(A* a). However, it has no problems understanding the extension to A* Run();

class A { };

class ExtendsA : public A { };

class B
{
public:
    virtual ~B(){}  
    virtual void Run(A* a) = 0;
    virtual A* Run() = 0;
};

class ExtendsB : public B
{
public:
    virtual ~ExtendsB(){}

    // Not OK! It does not see it as an implementation of 
    // virtual void Run(A* a) = 0;
    virtual void Run(ExtendsA* ea) {}; 
    virtual ExtendsA* Run() { return new ExtendsA(); }; // OK
};

Why C++ allows to change the return type to a sub-class, but not the parameter type?

Is it a good rationale or just a missed point in the language specifications?

4 Answers 4

17

Why C++ allows to change the return type to a sub-class, but not the parameter type?

C++ standard allows you to use a Covariant return type while overidding virtual functions but does not allow you to modify the function parameters.And yes there is a good rationale behind it.

Rationale:

Overriding essentially means that either the Base class method or the Derived class method will be called at run-time depending on the actual object pointed by the pointer.
It implies that:
i.e: "Every instance where the Base class method can be called can be replaced by call to Derived class method without any change to calling code."

If the above rule was not in place it would leave a window to break the existing code by addition of new functionality(new derived classes).

If you have a function prototype in derived class which differs from base class virtual function w.r.t parameters then the function does not override the base class function, since the above rule gets broken.

However, Covariant return types do not break this rule, because upcasting happens implicitly and a Base class pointer can always point to a derived class object without any casting, hence the Standard enforces this condition of covariant return types on return types.

Sign up to request clarification or add additional context in comments.

1 Comment

For that rule, arguments need not to match 100%. While covariant arguments would break that rule, contra variant arguments would not break it (every argument that can be passed to the base can also be passed to the derived type) and they are not allowed either. Part of the reason is that x-variance does not come for free and has to be handled by trampoline/thunks that fix the returned arguments (in the case of covariance). Allowing contra variance in the arguments would greatly (exponentially?) increase the required number of trampoline functions and the size of the virtual table.
9

The error is pretty clear: You need void Run(A*) in your class ExtendedB, but you don't have that. All you have is void Run(ExtendedA*), but that's not the same: The base class promises to accept any A-pointer, but your ExtendedB::Run is pickier and only accepts a narrow subset.

(You're confusing covariance and contravariance, but that is not relevant for C++, since C++ does not allow contravariant overrides.)

Comments

4

It's simply two different types, which makes it two distinct functions with two distinct signatures.

In general, if you're using a compiler that understands C++11, you should use the override keyword on functions that are intended to override another function. In your case, the error became apparent because of the abstract base class, but in other cases such an error can cause a lot of debugging...

Comments

2

Specialising the return type makes the subclass more strict - it will act as an A. However, restricting the Run method to accept only a subclass of the original argument makes B not act as an A.

Inheritance models an is-a relationship. Your code violates the Liskov Substitution principle.

1 Comment

In 2012, C++ must not be considered anymore a "pure OOP-by pointer" language (this was true only up to 1998). Today C++ inheritance is just an aggregation mechanism that CAN serve the Liskov substitution but may have other purposes as well. C++ classes are not necessarily OOP objects, hence inheritance is not necessarily an "is-a" relationship: it depends on the context. A more proper description is "is implicitly like a" and not "is a".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.