Reputation: 423
It is possible to override parameters with a derived class similar to what is show in B::g()
or B::h()
?
If not is there a sensible way to achieve the same thing?
It makes sense why B::i()
does not work, but why not the others?
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
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
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
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
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
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