Подтвердить что ты не робот

Понимание (просто?) Наследование С++

Я немного пытаюсь понять, почему этот фрагмент кода не компилируется.

#include <cstdio>

class A {
public:
    virtual int potential()=0;
    virtual int potential(int arg, int arg2)=0;
};

class B : public A {
public:
    int potential() { return 1; }
    virtual int potential(int arg, int arg2) { return 2; }
};

class C : public B {
public:
    int potential(int arg, int arg2) { return 3; }
};


int main(int argc, char** argv) {
    C c;
    int value = c.potential();
    printf("Got %i\n", value);
    return 0;
}

У меня есть два чистых виртуальных метода, названных potential в абстрактном суперклассе A. Подкласс B затем определяет оба, но дополнительный подкласс C должен только переопределить один из методов.

Однако при компиляции распознается только метод, определенный в C, и potential() не отображается (это должно быть унаследовано от B):

In function 'int main(int, char**)':
Line 23: error: no matching function for call to 'C::potential()'
compilation terminated due to -Wfatal-errors.

Если я переименую A::potential(int, int) на что-то еще полностью вниз по дереву наследования, например A::somethingElse(int, int), тогда код компилируется отлично, а результат Got 1, как и ожидалось.

Это подтверждено с помощью clang, g++ и MSVC cl.

Любые идеи о том, что происходит?

4b9b3361

Ответ 1

Однако при компиляции распознается только метод, определенный в C, а потенциал() не отображается (это должно быть унаследовано от B).

С++ не работает следующим образом: поскольку в C вы реализовали другой метод potential (метод с тем же именем, но с разными параметрами), другой метод скрыт до C.

Скрытие происходит из-за того, как С++ разрешает (перегружает) имена методов: когда вы вызываете метод potential в экземпляре класса (здесь C), С++ ищет в классе, является ли метод этого имени существует. Если это не так, он продолжает поиск в базовых классах. Он продвигается в иерархии до тех пор, пока не будет найден хотя бы один метод этого имени.

Но в вашем случае С++ не нужно искать далеко: метод уже существует в C, поэтому он останавливает поиск. Теперь С++ пытается сопоставить подпись метода. К сожалению, подпись метода не соответствует, но в это время ее слишком поздно: разрешение перегрузки выходит из строя; С++ не ищет другие методы, которые могут совпадать.

Существует три решения:

  • Импортируйте его с помощью using в C:

    class C : public B {
    public:
        using B::potential;
        int potential(int arg, int arg2) { return 3; }
    };
    
  • Вызвать метод из экземпляра базового класса в main:

    C c;
    B& b = c;
    int value = b.potential();
    
  • Определите имя явно в main:

    C c;
    int value = c.B::potential();
    

Ответ 2

Проблема заключается в скрытии имени.

Перегрузки функций и наследование функций не являются лучшими друзьями. Обычно вы или [хмм, что "либо" для трех?]:

  • Перегрузка функции в пределах одного класса. Все работает нормально.
  • Наследовать неперегруженную функцию из базового класса. Все работает нормально.
  • Восстановить неперегруженную функцию из базового класса B в производном классе C. C::func скрывает B::func, потому что имеет одно и то же имя.

Вы используете наследование и перегрузку, а ваш C::func скрывает B::func и каждую перегрузку, даже те, которые не были повторно реализованы в C.

Это немного странная путаница С++, но она легко разрешается.

Вкратце, решение состоит в том, чтобы принести B::potential() в область видимости для C с помощью оператора using:

class C : public B {
public:
    using B::potential; // brings overloads from B into scope
    int potential(int arg, int arg2) { return 3; }
};

Я написал статью здесь, которая демонстрирует проблему в глубину.