Функция с тем же именем, но другая подпись в производном классе

У меня есть функция с тем же именем, но с другой подписью в базовом и производном классах. Когда я пытаюсь использовать функцию базового класса в другом классе, который наследует от производного, я получаю сообщение об ошибке. См. Следующий код:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Я получаю следующую ошибку от компилятора gcc:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

Если я удалю int foo(int i){}; из класса B, или если я переименую его из foo1, все будет хорошо.

Какая проблема с этим?

Ответ 1

Функции в производных классах, которые не переопределяют функции в базовых классах, но имеют одно и то же имя, будут скрыть другие функции с одним и тем же именем в базовом классе.

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

Если вам нужно вызвать базовую функцию, вам понадобится охватить вызов, используя A::foo(s). Обратите внимание, что это также отключит любой механизм виртуальных функций для A::foo(string) в то же время.

Ответ 2

Это потому, что поиск имени останавливается, если он находит имя на одной из ваших баз. Он не будет смотреть дальше в других базах. Функция в B затеняет функцию в A. Вы должны повторно объявить функцию A в области B, чтобы обе функции были видны внутри B и C:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

Изменить: реальное описание, которое дает Стандарт (от 10.2/2):

Следующие шаги определяют результат поиска по имени в области класса, C. Сначала каждое объявление для имя в классе и в каждом из под-объектов базового класса. Имя члена f в одном суб- объект B скрывает имя члена f в под-объекте A, если A является под-объектом базового класса B. Любые объявления которые настолько скрыты, устраняются из рассмотрения. Каждая из этих деклараций, введенная использование-декларация считается от каждого под-объекта C, который имеет тип, содержащий декларацию- определяемое с помощью объявления-объявления .96) Если результирующий набор объявлений не все из под-объектов того же типа, или набор имеет нестатический член и включает в себя элементы из отдельных под-объектов, существует двусмысленность и программа плохо сформированы. В противном случае этот набор является результатом поиска.

В другом месте (прямо над ним) можно сказать следующее:

Для id-выражения [что-то вроде "foo" ] поиск имени начинается в области класса этого; для квалифицированного id [что-то вроде "A:: foo", A является вложенным именем-спецификатором], поиск имени начинается в области вложенного имени-спецификатора. Поиск имени происходит перед контролем доступа (3.4, раздел 11).

([...] поставил меня). Обратите внимание, что даже если ваш foo в B является приватным, foo в все равно не будет найден (потому что управление доступом происходит позже).