user1032677
user1032677

Reputation: 423

Overriding a member function with derived type in C++

My goal is to use r in B::g() as type Y.

struct X {};
struct Y : X {};

struct A {
    virtual void f(X q);
    virtual void g(X& r);
    virtual void h(X* s);

    virtual X i();  // note: overridden function is 'virtual X A::i()'
    virtual X* j();
};

struct B final : A {
    void f(Y q) final;   // error: 'void B::f(Y)' marked 'final', but is not virtual
    void g(Y& r) final;  // error: 'void B::g(Y&)' marked 'final', but is not virtual
    void h(Y* s) final;  // error: 'void B::h(Y*)' marked 'final', but is not virtual

    Y i() final;   // error: invalid covariant return type for 'virtual Y B::i()'
    Y* j() final;  // works
};


Upvotes: 1

Views: 2170

Answers (5)

Afshin
Afshin

Reputation: 9173

For overriding a function parameter list must be same, but in your case, it is not. That's why it is considered a new function and will not override function in parent.

So you need to have something like this for overriding:

struct A {
    virtual void f(X q);
    virtual void g(X& r);
    virtual void h(X* s);

    virtual X i();  
    virtual X* j();
};

struct B final : A {
    void f(X q) override final;   
    void g(X& r) override final;  
    void h(X* s) override final;  

    X i() override final;   
    X* j() override final;  
    // Y* j() override final;  <-- Also works because of covariant return
};

But remember that you still can pass Y to these functions because Y inherits X.

As a side note for the last class member, Y* j() override final; will also work, because it will be Covariant return. For covariant return, your return need to be pointer or reference. But you need to see if you really need it or not.

Upvotes: 0

Peter
Peter

Reputation: 36607

Overriding a virtual function can only be done if the overriding function has the same signature (type of arguments, const qualifiers, etc) as the inherited one.

So

 struct A
 {
      virtual void f(X &);
 };

 struct B : public A
 {
      virtual void f(X &);
 };

is possible but changing the signature of f(), as in

 struct B : public A
 {
      virtual void f(Y &);
 };

actually hides the inherited f() rather than overriding it. With the override identifer, the compiler would detect that this is not overriding, and diagnose an error.

It is possible to pass a Y to A::f() or to B::f() if correctly overridden, since your Y is derived from X. But it is not possible to override A::f() with a function that has any argument of different type from A::f().

There is a special case for a function that returns a pointer (or, alternatively, a reference) to a base class - this can be overridden by a function that returns a pointer to a derived class. The constraint that the arguments must have the same type(s). So your B::j() works since Y is derived from X. As far as the compiler is concerned, a call like

 X*x = pA->j();    // pA is of type A* but points at a B

is okay since it still returns a pointer that can be validly converted to X *

Upvotes: 0

r3mus n0x
r3mus n0x

Reputation: 6144

You can only override virtual functions in a way that still makes it safe to use your derived class in place of the base class (e.g. via pointer to the base).

Let's take the following example with the classes you provided:

B b;
A *a = &b;

X x;
a->g(x);

In the last line there is no way to ensure that the caller of g() will pass Y as expected by B because the caller is using the base class' interface which is the whole point of virtual functions and dynamic polymorphism in general.

On the other hand, it is safe for a return type to be more specific in the derived class:

B b;
A *a = &b;
X *x = a->j();

The last statement is still type-safe even if j() actually returns Y*. This C++ feature is called covariant return type.

You can also read more about covariance and contravariance in type systems here.

Upvotes: 1

xBm
xBm

Reputation: 69

To override that are marked as final you need to add virtual at the beginning.

For example: virtual void f(Y q) final;

B::i() doesn't work because the return type is not identical to the return type of the function you are trying to override.

But the reason B::j() does work is because the return type is a pointer, which you can technically return a pointer to every type there is.

Upvotes: 0

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122575

My goal is to use r in B::g() as type Y.

You can do that, but then B::g does not override A::g. If that should be the case, you need to use the same parameter types:

struct A {
    virtual void g(X& r);
    virtual ~A(){}
};

struct B : A {
    void g(X& r) override;
};

Upvotes: 0

Related Questions