Osama Ahmad
Osama Ahmad

Reputation: 2096

How to return a templated derived class instead of base class in an overridden function?

#include <iostream>

template <typename T>
class A {};

template <typename T>
class B : public A<T> {};

class C
{
    virtual A<int> func() = 0;
};

class D : public C
{
    virtual B<int> func() override
    {
        return B<int>{};
    }
};

Here B inherits from A and the template argument is int for both of the return values. Why can't I just return B<int> instead of A<int>?

Upvotes: 0

Views: 692

Answers (4)

Ojd2000
Ojd2000

Reputation: 56

From cppreference:

If the function Derived::f overrides a function Base::f, their return types must either be the same or be covariant.

There are 3 conditions to be all satisfied for two types to be covariant, and one of them is that they need to be either a pointer type or a reference type.

A<int> and B<int> are neither the same type nor covariant types, which implies that you cannot use B<int> as the return type of your overridden method.

Please also notice that you don't have to use virtual specifier in your overridden method.

Moreover, as also other people noticed, the following will work and return an A<int>-typed object:

A<int> func() override
{
    return B<int>{};
}

But you should avoid it. In fact, after a B<int> object is created, it will be sliced into an A<int> type, for the reasons we have seen, meaning your object type will effectively be simply A<int>, and not a B<int>'s instance. This means that you uselessly created a B<int> instance when you could have just created an A<int>.

If you need a B<int>, you'll need to return either a pointer or a reference.

Upvotes: 1

SeventhSon84
SeventhSon84

Reputation: 364

B D::func() cannot be an override of AC::func(), because the return type of those two function are different from each other.

Upvotes: 1

merlinND
merlinND

Reputation: 855

When declaring B, you must inherit from an actual class, not a template:

template <typename T>
class B : public A<T> {};

The second issue is that you cannot change the signature of func() when overriding it in class D. However, once that's fixed, you will be able to return B<int>{} as expected.

Edit: a third problem pointed out by @Josh Wilson is that you will not get the behavior of a B from the return value of func() because it specifies an A returned by value. You need to return a pointer to A<int> for polymorphism to apply.

Upvotes: 1

joshwilsonvu
joshwilsonvu

Reputation: 2679

As merlinND pointed out, you have to inherit from class A<T>. But it will still fail unless you return a pointer or reference to B instead of a value type. The following will compile.

#include <iostream>

template <typename T>
class A {};

template <typename T>
class B : public A<T> {};

class C
{
    virtual A<int>* func() = 0;
};

class D : public C
{
    virtual B<int>* func() override
    {
        return new B<int>{};
    }
};

Upvotes: 1

Related Questions