Как вернуть шаблонный производный класс вместо базового класса в переопределенной функции? - PullRequest
0 голосов
/ 17 апреля 2020
#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>{};
    }
};

Здесь B наследуется от A, а аргумент шаблона равен int для обоих возвращаемых значений. Почему я не могу просто вернуть B<int> вместо A<int>?

Ответы [ 4 ]

1 голос
/ 17 апреля 2020

С cppreference :

Если функция Derived::f переопределяет функцию Base::f, их типы возврата должны быть одинаковыми или ковариантными.

Существует три условия, которые должны быть выполнены для ковариантности двух типов, и одно из них заключается в том, что они должны быть либо указателем, либо ссылочным типом.

A<int> и B<int> не являются ни тем же типом, ни ковариантным типом, что означает, что вы не можете использовать B<int> в качестве типа возврата вашего переопределенного метода.

Обратите внимание, что вам не нужно использовать спецификатор virtual в вашем переопределенном методе.

Более того, как заметили и другие, сработает следующее и вернет объект A<int>:

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

Но вам следует избегать этого. Фактически, после создания B<int> объекта он будет нарезан на тип A<int>, по причинам, которые мы видели, то есть тип вашего объекта будет просто A<int>, а не экземпляр B<int>. Это означает, что вы бесполезно создали экземпляр B<int>, когда вы могли просто создать A<int>.

Если вам нужен B<int>, вам нужно будет вернуть либо указатель, либо ссылку.

1 голос
/ 17 апреля 2020

Как указывал merlinND, вы должны наследовать от class A<T>. Но он все равно потерпит неудачу, если вы не вернете указатель или ссылку на B вместо типа значения. Будет скомпилировано следующее.

#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>{};
    }
};
1 голос
/ 17 апреля 2020

BD :: fun c () не может быть переопределением A C :: fun c (), потому что тип возвращаемого значения этих двух функций отличается друг от друга.

1 голос
/ 17 апреля 2020

При объявлении B вы должны наследовать от фактического класса, а не от шаблона:

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

Вторая проблема заключается в том, что вы не можете изменить сигнатуру func() при ее переопределении в классе D , Однако, как только это будет исправлено, вы сможете вернуть B<int>{}, как и ожидалось.

Редактировать: третья проблема, указанная @ Jo sh Уилсоном, заключается в том, что вы не получите поведение B из возвращаемого значения func(), поскольку оно указывает A, возвращаемое по значению. Вам нужно вернуть указатель на A<int> для применения полиморфизма.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...