Warum werden mehrfach geerbte Funktionen mit demselben Namen, aber unterschiedlichen Signaturen nicht als überladene Funktionen behandelt?

Lesezeit: 4 Minuten

Warum werden mehrfach geerbte Funktionen mit demselben Namen aber unterschiedlichen
Xeo

Das folgende Snippet erzeugt während der Kompilierung einen „ambigious call to foo“-Fehler, und ich würde gerne wissen, ob es eine Möglichkeit gibt, dieses Problem zu umgehen, ohne den Aufruf von foo vollständig zu qualifizieren:

#include <iostream>

struct Base1{
    void foo(int){
    }
};

struct Base2{
    void foo(float){
    }
};

struct Derived : public Base1, public Base2{
};

int main(){
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;
}

Frage ist also wie der Titel schon sagt. Ideen? Ich meine, folgendes funktioniert einwandfrei:

#include <iostream>

struct Base{
    void foo(int){
    }
};

struct Derived : public Base{
    void foo(float){
    }
};

int main(){
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;
}

  • Fügen Sie Logging-Anweisungen innerhalb der beiden foo (im zweiten Fall) zu der Konstante hinzu, welche Funktion aufgerufen wird, Sie werden überrascht sein … C++ ist voller geheimnisvoller Regeln;)

    – Matthias M.

    20. März 2011 um 14:17 Uhr

  • @Matthieu: keuchen! Verdammte Versteckregeln. 🙁

    – Xeo

    20. März 2011 um 14:27 Uhr

Warum werden mehrfach geerbte Funktionen mit demselben Namen aber unterschiedlichen
Prasun Saurav

Member-Lookup-Regeln sind in Abschnitt 10.2/2 definiert

Die folgenden Schritte definieren das Ergebnis der Namenssuche in einem Klassenbereich, C. Zuerst wird jede Deklaration für den Namen in der Klasse und in jedem ihrer Unterobjekte der Basisklasse betrachtet. Ein Mitgliedsname f in einem Unterobjekt B verbirgt einen Mitgliedsnamen f in einem Unterobjekt A wenn A ist ein Unterobjekt der Basisklasse von B. Derart versteckte Erklärungen werden nicht berücksichtigt. Jede dieser Deklarationen, die durch eine using-Deklaration eingeleitet wurde, wird als von jedem Unterobjekt von betrachtet C das ist von dem Typ, der die durch die using-Deklaration bezeichnete Deklaration enthält. Wenn der resultierende Satz von Deklarationen nicht alle von Unterobjekten des gleichen Typs stammt oder der Satz ein nicht statisches Mitglied hat und Mitglieder von unterschiedlichen Unterobjekten enthält, liegt eine Mehrdeutigkeit vor und das Programm ist falsch formatiert. Andernfalls ist diese Menge das Ergebnis der Suche.

class A {
public:
  int f(int);

};
class B {
public:
   int f();

};
class C : public A, public B {};
int main()
{
     C c;
     c.f(); // ambiguous
}

Sie können also die verwenden using Erklärungen A::f und B::f um diese Zweideutigkeit aufzulösen

class C : public A, public B {
     using A::f;
     using B::f;

};

int main()
{
     C c;
     c.f(); // fine
}

Der zweite Code funktioniert einwandfrei, weil void foo(float) liegt im Geltungsbereich von C. Tatsächlich d.foo(5); Anrufe void foo(float) und nicht die int Ausführung.

  • Die void foo(float) Die aufgerufene Version hat mich wirklich dorthin gebracht. Vielen Dank für Ihre ausführliche Antwort. 🙂

    – Xeo

    20. März 2011 um 14:31 Uhr

  • Eine Sache, die mir in den Sinn kommt … gibt es eine Situation, in der man die Funktionen der Basisklasse verbergen möchte, wenn sie unterschiedliche Signaturen haben? Für gleiche Signaturfunktionen ist das sicher nützlich, aber für andere kann ich mir einfach kein gutes Beispiel vorstellen …

    – Xeo

    21. März 2011 um 20:21 Uhr

  • Wenn der resultierende Satz von Deklarationen … Elemente aus unterschiedlichen Unterobjekten enthält, liegt eine Mehrdeutigkeit vor, und das Programm ist falsch formatiert Was ist der Grund für diese Regel? Beispiele dafür, was sonst passieren würde, wären schön.

    – Olumid

    3. März 2016 um 22:29 Uhr

Namenssuche ist eine separate Phase zu Überlastungsauflösung.

Die Namenssuche findet zuerst statt. Das ist der Prozess der Entscheidung, für welchen Geltungsbereich der Name gilt. In diesem Fall müssen wir entscheiden, ob d.foo bedeutet d.D::foooder d.B1::foooder d.B2::foo. Die Namenssuchregeln berücksichtigen keine Funktionsparameter oder ähnliches; es geht nur um Namen und Geltungsbereiche.

Erst nachdem diese Entscheidung getroffen wurde, führen wir die Überladungsauflösung für die verschiedenen Überladungen der Funktion in dem Bereich durch, in dem der Name gefunden wurde.

In Ihrem Beispiel anrufen d.foo() würde finden D::foo() wenn es eine solche Funktion gäbe. Aber es gibt keine. Also, indem es die Bereiche rückwärts hocharbeitet, probiert es die Basisklassen aus. Jetzt foo könnte ebenso aufschauen B1::foo oder B2::foo also zweideutig.

Aus dem gleichen Grund erhalten Sie Mehrdeutigkeit, die unqualifiziert anruft foo(5); in einem D Mitgliedsfunktion.


Die Wirkung der empfohlenen Lösung:

struct Derived : public Base1, public Base2{
    using Base1::foo;
    using Base2::foo;

ist, dass dadurch der Name entsteht D::foo, und lässt es zwei Funktionen identifizieren. Das Ergebnis ist das d.foo beschließt zu d.D::foound dann kann es bei diesen beiden Funktionen, die durch gekennzeichnet sind, zu einer Überlastungsauflösung kommen D::foo.

Hinweis: In diesem Beispiel D::foo(int) und Base1::foo(int) sind zwei Bezeichner für die eine Funktion; aber im Allgemeinen macht es für den Namenssuch- und Überladungsauflösungsprozess keinen Unterschied, ob es sich um zwei separate Funktionen handelt oder nicht.

1646924408 530 Warum werden mehrfach geerbte Funktionen mit demselben Namen aber unterschiedlichen
a1ex07

Wird es für Sie funktionieren?

struct Derived : public Base1, public Base2{
   using Base2::foo;}

988270cookie-checkWarum werden mehrfach geerbte Funktionen mit demselben Namen, aber unterschiedlichen Signaturen nicht als überladene Funktionen behandelt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy