Функція з тим самим іменем, але різним підписом у похідному класі


92

У мене є функція з тим самим іменем, але з різним підписом у базовому та похідних класах. Коли я намагаюся використовувати функцію базового класу в іншому класі, який успадковується від похідного, я отримую повідомлення про помилку. Дивіться наступний код:

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
Технічно дублікат цього запитання, але це питання має кращу назву та відповіді.
Трубадур,

Відповіді:


79

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

Зазвичай вважається поганою практикою мати функції в похідних класах, які мають те саме ім'я, що і функції в класі басів, які не призначені замінювати функції базового класу, оскільки те, що ви бачите, зазвичай не є бажаною поведінкою. Зазвичай переважно давати різним функціям різні імена.

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


13
також прочитайте відповідь litdb: ви можете "показати" базову функцію, використовуючи речення A :: foo в B.
xtofl

Правда, я просто розглядав рішення, яке можна було б використовувати на сайті виклику, розглядаючи базову ієрархію як виправлену.
CB Bailey

2
Що лежить в основі цього твердження, за яким слідує порада: "Зазвичай вважається поганою практикою мати функції у похідних класах, які мають те саме ім'я, що і функції в класі басів, які не призначені замінювати функції базового класу те, що ви бачите, зазвичай не є бажаною поведінкою. Зазвичай переважно давати різним функціям різні імена " . Що робити, якщо вони семантично роблять одне і те ж? C ++ надає вам рішення проблеми, спричиненої цим, як пояснюється відповіддю Йоганнеса.
Наваз

109

Це тому, що пошук імен зупиняється, якщо він знаходить ім’я в одній з ваших баз. Це не буде виглядати далі в інших базах. Функція в 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. Будь-які оголошення, які так приховані, вилучаються з розгляду. Кожне з цих оголошень, яке було введено за допомогою декларації using, вважається таким, що надходить від кожного під-об'єкта С, що має тип, що містить декларацію, позначену декларацією use.96) Якщо отриманий набір декларацій не є все з суб-об’єктів одного типу, або набір має нестатичний член і включає члени з різних суб-об’єктів, існує неоднозначність і програма неправильно сформована. В іншому випадку цей набір є результатом пошуку.

Він має сказати наступне в іншому місці (трохи вище нього):

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

([...] поставлений мною). Зауважте, що це означає, що навіть якщо ваш foo в B приватний, foo в A все одно не буде знайдений (оскільки контроль доступу відбувається пізніше).


litb, дякую за вашу відповідь. Але коли я намагаюся скомпілювати код, я отримую: не може регулювати доступ до void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in класу B «з - за локального метода` INT B :: Foo (Int)» з таким же ім'ям. Можливо, це тому, що я використовую стару версію gcc
Ігор Окс

1
так, точно помилка компілятора. старі компілятори використовували "A :: foo;" замість "використання A :: foo;" але перша застаріла в C ++.
Йоханнес Шауб - літб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.