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

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

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, все будет нормально.

Что с этим не так?


person Igor Oks    schedule 04.01.2009    source источник
comment
Технически это дубликат этого вопроса, но у этого есть лучшее название и ответы.   -  person Troubadour    schedule 02.09.2011


Ответы (2)


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

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

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

person CB Bailey    schedule 04.01.2009
comment
также прочитайте ответ litdb: вы можете `` показать '' базовую функцию с помощью предложения 'using A :: foo' в B. - person xtofl; 04.01.2009
comment
Правда, я просто искал решение, которое можно было бы использовать на сайте вызова, рассматривая базовую иерархию как фиксированную. - person CB Bailey; 05.01.2009
comment
На чем основано это утверждение и что следует совет: Обычно считается плохой практикой иметь функции в производных классах, которые имеют то же имя, что и функции в классе баса, которые не t предназначен для переопределения функций базового класса, поскольку то, что вы видите, обычно нежелательно. Обычно предпочтительнее давать разным функциям разные имена. Что, если семантически они делают то же самое? С ++ предоставляет вам решение проблемы, вызванной этим, однако, как объясняет ответ Йоханнеса. - person Nawaz; 13.04.2018

Это потому, что поиск имени останавливается, если он находит имя в одной из ваших баз. В других базах он не будет выходить за рамки. Функция в 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"] поиск имени начинается в области класса this; для квалифицированного-идентификатора [что-то вроде "A :: foo", A - вложенный-указатель-имени] поиск имени начинается в области действия вложенного-указателя-имени. Поиск имени выполняется до управления доступом (3.4, раздел 11).

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

person Johannes Schaub - litb    schedule 04.01.2009
comment
litb, спасибо за ответ. Но когда я пытаюсь скомпилировать ваш код, я получаю: не могу настроить доступ к void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in class B 'из-за локального метода `int B :: foo (int)' с тем же именем. Может это потому, что я использую старую версию gcc - person Igor Oks; 04.01.2009
comment
да, определенно ошибка компилятора. старые компиляторы использовали A :: foo; вместо использования A :: foo; но первый не рекомендуется в C ++. - person Johannes Schaub - litb; 04.01.2009