Funktion mit gleichem Namen, aber unterschiedlicher Signatur in der abgeleiteten Klasse

90

Ich habe eine Funktion mit demselben Namen, aber mit unterschiedlicher Signatur in einer Basisklasse und abgeleiteten Klassen. Wenn ich versuche, die Funktion der Basisklasse in einer anderen Klasse zu verwenden, die von der abgeleiteten erbt, wird eine Fehlermeldung angezeigt. Siehe folgenden Code:

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

Ich erhalte den folgenden Fehler vom gcc-Compiler:

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

Wenn ich int foo(int i){};aus der Klasse entferne Boder aus umbenenne foo1, funktioniert alles einwandfrei.

Was ist das Problem damit?

Igor Oks
quelle
1
Technisch gesehen ein Duplikat dieser Frage, aber diese hat einen besseren Titel und bessere Antworten.
Troubadour

Antworten:

77

Funktionen in abgeleiteten Klassen, die Funktionen in Basisklassen nicht überschreiben, aber denselben Namen haben, verbergen andere Funktionen mit demselben Namen in der Basisklasse.

Es wird allgemein als schlechte Praxis angesehen, Funktionen in abgeleiteten Klassen zu haben, die denselben Namen haben wie Funktionen in der Bassklasse, die nicht dazu gedacht sind, die Funktionen der Basisklasse zu überschreiben, da das, was Sie sehen, normalerweise kein wünschenswertes Verhalten ist. Es ist normalerweise vorzuziehen, verschiedenen Funktionen unterschiedliche Namen zu geben.

Wenn Sie die Basisfunktion aufrufen müssen, müssen Sie den Aufruf mit verwenden A::foo(s). Beachten Sie, dass dadurch auch alle virtuellen Funktionsmechanismen gleichzeitig deaktiviert werden A::foo(string).

CB Bailey
quelle
13
Sie kann ‚sichtbar machen‘ die Basisfunktion von einem ‚mit A :: foo‘ Klausel in B.: auch litdb Antwort lesen
xtofl
Es stimmt, ich habe nur nach einer Lösung gesucht, die am Anrufort verwendet werden kann, und die Basishierarchie als fest behandelt.
CB Bailey
2
Was ist die Grundlage dieser Behauptung und gefolgt von dem Rat: "Es wird allgemein als schlechte Praxis angesehen , Funktionen in abgeleiteten Klassen zu haben, die denselben Namen haben wie Funktionen in der Bassklasse, die nicht dazu gedacht sind, die Funktionen der Basisklasse als zu überschreiben Was Sie sehen, ist normalerweise kein wünschenswertes Verhalten. Es ist normalerweise vorzuziehen, verschiedenen Funktionen unterschiedliche Namen zu geben . " Was ist, wenn sie semantisch dasselbe tun? C ++ bietet Ihnen jedoch eine Lösung für das dadurch verursachte Problem, wie in Johannes 'Antwort erläutert.
Nawaz
107

Dies liegt daran, dass die Namenssuche beendet wird, wenn in einer Ihrer Basen ein Name gefunden wird. In anderen Basen wird es nicht weiter schauen. Die Funktion in B schattiert die Funktion in A. Sie müssen die Funktion von A im Bereich von B neu deklarieren, damit beide Funktionen innerhalb von B und C sichtbar sind:

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

Bearbeiten: Die eigentliche Beschreibung des Standards lautet (ab 10.2 / 2):

Die folgenden Schritte definieren das Ergebnis der Namenssuche in einem Klassenbereich C. Zunächst wird jede Deklaration für den Namen in der Klasse und in jedem ihrer Basisklassen-Unterobjekte berücksichtigt. Ein Mitgliedsname f in einem Unterobjekt B verbirgt einen Mitgliedsnamen f in einem Unterobjekt A, wenn A ein Unterobjekt der Basisklasse von B ist. Alle so ausgeblendeten Deklarationen werden nicht berücksichtigt. Jede dieser Deklarationen, die durch eine using-Deklaration eingeführt wurden, stammt von jedem Unterobjekt von C, das vom Typ ist, der die in der using-Deklaration angegebene Deklaration enthält.96) Wenn die resultierende Menge von Deklarationen nicht vorhanden ist Alle von Unterobjekten des gleichen Typs oder die Menge hat ein nicht statisches Element und enthält Elemente von verschiedenen Unterobjekten. Es besteht eine Mehrdeutigkeit und das Programm ist schlecht geformt. Andernfalls ist diese Menge das Ergebnis der Suche.

An einer anderen Stelle (direkt darüber) hat es Folgendes zu sagen:

Für einen ID-Ausdruck [so etwas wie "foo" ] beginnt die Namenssuche im Klassenumfang. Für eine qualifizierte ID [so etwas wie "A :: foo", A ist ein verschachtelter Namensbezeichner ] beginnt die Namenssuche im Bereich des verschachtelten Namensbezeichners. Die Namenssuche erfolgt vor der Zugriffskontrolle (3.4, Abschnitt 11).

([...] von mir gestellt). Beachten Sie, dass selbst wenn Ihr Foo in B privat ist, das Foo in A immer noch nicht gefunden wird (da die Zugriffskontrolle später erfolgt).

Johannes Schaub - litb
quelle
litb, danke für deine antwort. Wenn ich jedoch versuche, Ihren Code zu kompilieren, wird Folgendes angezeigt: Der Zugriff auf void A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in Klasse B kann aufgrund der lokalen Methode "int B :: foo (int)" mit demselben Namen nicht angepasst werden. Vielleicht liegt es daran, dass ich eine alte Version von gcc verwende
Igor Oks
1
Ja, definitiv ein Compiler-Fehler. alte Compiler verwendeten "A :: foo;" anstelle von "using A :: foo;" Ersteres ist jedoch in C ++ veraltet.
Johannes Schaub - litb