Производный класс абстрактного базового класса неправильно переопределяет базовый чисто виртуальный метод

Итак, я пытаюсь создать производный класс абстрактного базового класса, но чистые виртуальные методы не могут быть объявлены с использованием класса Derived.

template<class T>
class Foo
{
    public:
        virtual Foo<T>* add(const Foo<T>* rhs) = 0; 
};

template<class T>
class Doo : public Foo<T>
{
    public:
        Doo<T>* add(const Doo<T>* rhs)
        {
            return this;
        }
};

int main()
{
    Doo<double> d;
    return 0;
}

Я ожидал, что объявление метода добавления в Doo сработает, потому что Doo является подклассом Foo, но g++ говорит, что я не переопределяю метод добавления Foo's. Я предполагаю, что это что-то простое с тем, как я объявляю метод добавления Doo's.

один берет Foo другой Doo, это разные методы

463035818_is_not_a_number 09.04.2019 20:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
41
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Подписи не совпадают, поэтому вы ничего не переопределяете. Найдите ключевое слово override, чтобы ваш компилятор помог вам диагностировать подобные ошибки.

add(const Foo<T>* rhs)

не то же самое, что

add(const Doo<T>* rhs)

Они приводят разные аргументы.

Они также возвращают разные типы. Но возвращаемые типы не имеют значения при разрешении перегрузки или при переопределении, но обычно это также признак того, что вы пропустили отметку (того, что вы хотите переопределить), поскольку как правило вы хотите, чтобы ваша переопределяющая функция возвращала тот же тип, что и то, что она переопределяет. .

Я видел, что это решит проблему, но меня беспокоит то, что любой другой класс, производный от Foo, может быть передан в этот метод. Является ли единственным решением убедиться, что унаследованные объекты не нарушают родительские требования?

evader110 09.04.2019 21:08

@ evader110 Извините, мне не совсем понятно, о чем вы там спрашиваете.

Jesper Juhl 09.04.2019 21:11

Если бы я создал другой производный класс с именем Goo, производным от Foo, работал бы он, если бы я передал его в метод добавления Doo? Если да, то будет ли создание невиртуального метода в Doo единственным способом избежать такого поведения?

evader110 09.04.2019 21:25

@ evader110 Объявляя чисто виртуальный add метод, Foo обещает, что все производные классы будут иметь add метод, который принимает указатель на Foo. Метод add, который накладывает какие-либо дополнительные ограничения на свой параметр, не выполняет это обещание и, следовательно, не считается реализацией этого чисто виртуального метода.

Miles Budnek 09.04.2019 21:35

Используя функцию с другой сигнатурой, вы скрываете функцию, а не переопределяете ее.

Лучший способ защитить себя от этих ошибок — использовать ключевое слово override следующим образом (в вашем примере) каждый раз, когда вы собираетесь переопределить виртуальную функцию:

Doo<T>* add(const Doo<T>* rhs) override

И тогда компилятор выдаст ошибку, если функция фактически не переопределяет.

Другие вопросы по теме