Funkcja o tej samej nazwie, ale innej sygnaturze w klasie pochodnej

92

Mam funkcję o tej samej nazwie, ale z innym podpisem w klasach bazowych i pochodnych. Kiedy próbuję użyć funkcji klasy bazowej w innej klasie, która dziedziczy z klasy pochodnej, pojawia się błąd. Zobacz poniższy kod:

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);
    }
};

Otrzymuję następujący błąd z kompilatora gcc:

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

Jeśli usunę int foo(int i){};z zajęć Blub zmienię jego nazwę z foo1, wszystko działa dobrze.

Jaki jest z tym problem?

Igor Oks
źródło
1
Technicznie jest duplikatem tego pytania, ale to ma lepszy tytuł i odpowiedzi.
Troubadour

Odpowiedzi:

79

Funkcje w klasach pochodnych, które nie przesłaniają funkcji w klasach bazowych, ale mają taką samą nazwę, będą ukrywać inne funkcje o tej samej nazwie w klasie bazowej.

Generalnie uważa się, że posiadanie funkcji w klasach pochodnych, które mają taką samą nazwę jak funkcje w klasie basu, a które nie mają zastępować funkcji klasy bazowej, jest złą praktyką, ponieważ to, co widzisz, nie jest zwykle pożądanym zachowaniem. Zwykle lepiej jest nadać różnym funkcjom różne nazwy.

Jeśli chcesz wywołać funkcję podstawową, musisz określić zakres połączenia przy użyciu A::foo(s). Zauważ, że to również wyłączyłoby jakikolwiek mechanizm funkcji wirtualnych A::foo(string)w tym samym czasie.

CB Bailey
źródło
13
przeczytaj również odpowiedź litdb: możesz „odkryć” funkcję podstawową za pomocą klauzuli „using A :: foo” w B.
xtofl
To prawda, po prostu szukałem rozwiązania, które można by zastosować w miejscu wywołania, traktując podstawową hierarchię jako ustaloną.
CB Bailey
2
Jaka jest podstawa tego twierdzenia, po którym następuje rada: „Generalnie uważa się za złą praktykę posiadanie funkcji w klasach pochodnych, które mają taką samą nazwę jak funkcje w klasie basu, które nie mają zastępować funkcji klasy bazowej to, co widzisz, nie jest zwykle pożądanym zachowaniem. Zwykle lepiej jest nadać różnym funkcjom różne nazwy . Co jeśli robią semantycznie to samo? C ++ zapewnia jednak rozwiązanie problemu przez to spowodowanego, jak wyjaśniono w odpowiedzi Johannesa.
Nawaz
109

Dzieje się tak, ponieważ wyszukiwanie nazw zatrzymuje się, jeśli znajdzie nazwę w jednej z twoich baz. W innych bazach nie będzie wyglądać dalej. Funkcja w B pomocniczym funkcję w A. należy ponownie zadeklarować funkcję w zakresie b, tak, że obie te funkcje są widoczne od wewnątrz B i 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);
    }
};

Edycja: rzeczywisty opis podany przez standard to (od 10.2 / 2):

Poniższe kroki definiują wynik wyszukiwania nazwy w zakresie klasy, C. Po pierwsze, brana jest pod uwagę każda deklaracja nazwy w klasie i każdym z jej podobiektów klasy bazowej. Nazwa elementu f w jednym podobiekcie B ukrywa nazwę elementu f w podobiekcie A, jeśli A jest podobiektem klasy bazowej B. Wszelkie deklaracje, które są tak ukryte, są eliminowane z rozważań. Każda z tych deklaracji, która została wprowadzona przez deklarację using, jest uważana za pochodzącą z każdego podobiektu C, który jest typu zawierającego deklarację wyznaczoną przez deklarację using. 96) Jeśli wynikowy zestaw deklaracji nie jest wszystkie z podobiektów tego samego typu lub zbiór ma niestatycznego członka i zawiera elementy składowe z różnych podobiektów, występuje niejednoznaczność i program jest źle sformułowany. W przeciwnym razie ten zestaw jest wynikiem wyszukiwania.

W innym miejscu (tuż nad nim) ma do powiedzenia:

W przypadku wyrażenia id [ coś w rodzaju „foo” ], wyszukiwanie nazw zaczyna się w zakresie klasy this; dla kwalifikowanego identyfikatora [ coś w rodzaju „A :: foo”, A jest zagnieżdżonym specyfikatorem-nazwy ], wyszukiwanie nazw zaczyna się w zakresie zagnieżdżonego specyfikatora-nazwy. Wyszukiwanie nazwy ma miejsce przed kontrolą dostępu (3.4, klauzula 11).

([...] podane przeze mnie). Zauważ, że oznacza to, że nawet jeśli twoje foo w B jest prywatne, foo w A nadal nie zostanie znalezione (ponieważ kontrola dostępu ma miejsce później).

Johannes Schaub - litb
źródło
litb, dziękuję za odpowiedź. Ale gdy próbuję skompilować kod, otrzymuję: Nie można regulować dostęp do void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in klasy B „ponieważ metody lokalnej` int B :: foo (int)”z tej samej nazwie. Może dlatego, że używam starej wersji gcc
Igor Oks
1
tak, zdecydowanie błąd kompilatora. stare kompilatory używały "A :: foo;" zamiast "using A :: foo;" ale ta pierwsza jest przestarzała w C ++.
Johannes Schaub - litb