c ++ überladene virtuelle Funktionswarnung durch Klirren?

79

clang gibt beim Kompilieren des folgenden Codes eine Warnung aus:

struct Base
{
    virtual void * get(char* e);
//    virtual void * get(char* e, int index);
};

struct Derived: public Base {
    virtual void * get(char* e, int index);
};

Die Warnung lautet:

warning: 'Derived::get' hides overloaded virtual function [-Woverloaded-virtual]

(Diese Warnung muss natürlich aktiviert sein).

Ich verstehe nicht warum. Beachten Sie, dass das Kommentieren derselben Deklaration in Base die Warnung beendet. Mein Verständnis ist, dass es kein Verstecken geben kann, da die beiden Funktionen get () unterschiedliche Signaturen haben.

Ist Klirren richtig? Warum?

Beachten Sie, dass dies unter MacOS X erfolgt und eine aktuelle Version von Xcode ausgeführt wird.

clang --version
Apple LLVM version 5.0 (clang-500.1.74) (based on LLVM 3.3svn)

Update: Gleiches Verhalten mit Xcode 4.6.3.

Jean-Denis Muys
quelle

Antworten:

114

Diese Warnung soll verhindern, dass Überlastungen versehentlich ausgeblendet werden, wenn ein Überschreiben beabsichtigt ist. Betrachten Sie ein etwas anderes Beispiel:

struct chart; // let's pretend this exists
struct Base
{
    virtual void* get(char* e);
};

struct Derived: public Base {
    virtual void* get(chart* e); // typo, we wanted to override the same function
};

Da es sich um eine Warnung handelt, bedeutet dies nicht unbedingt, dass es sich um einen Fehler handelt, aber es kann sich um einen Fehler handeln. Normalerweise haben solche Warnungen die Möglichkeit, sie auszuschalten, indem sie expliziter sind und den Compiler wissen lassen, dass Sie beabsichtigt haben, was Sie geschrieben haben. Ich glaube, in diesem Fall können Sie Folgendes tun:

struct Derived: public Base {
    using Base::get; // tell the compiler we want both the get from Base and ours
    virtual void * get(char* e, int index);
};
R. Martinho Fernandes
quelle
11
Es kann darauf hingewiesen werden, dass diese Lösung zum "lokalen Ausschalten der Warnung" auch die Semantik des Codes ändert: Sie können das getFunktionselement jetzt mit einem einzelnen Argument für ein Objekt vom statischen Typ aufrufen Derived. Ohne die using-Deklaration würde dasselbe zu einem Kompilierungsfehler führen.
Ad N
28

Eine andere Möglichkeit, die Warnung zu deaktivieren und die öffentliche Struktur der Struktur intakt zu halten, wäre:

struct Derived: public Base
{
    virtual void * get(char* e, int index);
private:
    using Base::get;
};

Dadurch kann ein Verbraucher nicht Derivedanrufen, Derived::get(char* e)während die Warnung stummgeschaltet wird:

Derived der;
der.get("", 0); //Allowed
der.get("");    //Compilation error
Pedro
quelle
2
Dies ist definitiv ein sicherer Weg, um diese Warnung zu entfernen, wenn Sie vorhatten, die Klassenmethode der Basis zu ersetzen get!
jpo38
Achten Sie jedoch auf Fälle, in denen dies zu einem mehrdeutigen Anruf führen würde. Diese Lösung ist also auch nicht 100% sicher.
Sigy
22

Die Lösung von R. Martinho Fernandes ist absolut gültig, wenn Sie die get()Methode tatsächlich mit einem einzelnen char * -Argument einbringen möchtenDerived Geltungsbereich .

In dem von Ihnen bereitgestellten Snippet sind keine virtuellen Methoden erforderlich (da Base und Derived keine Methode mit derselben Signatur gemeinsam nutzen).

Unter der Annahme, dass tatsächlich Polymorphismus erforderlich ist, könnte das Versteckverhalten dennoch das sein, was beabsichtigt ist. In diesem Fall ist es möglich, Clangs Warnung mit folgendem Pragma lokal zu deaktivieren:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
    // Member declaration raising the warning.
#pragma clang diagnostic pop
Anzeige N.
quelle
1
Diese Antwort war doppelt erstaunlich. Zuerst war es die genaue Antwort auf das, wonach ich suchte, dass ich "meine Methode da draußen haben wollte". Als ich aus Gründen des Pragmas einen Kommentar in meinen Code schrieb und wie dumm das Klirren war, fiel mir auf, dass ich Override schrieb, aber die Warnung war überladen. Dann klickte ich und stellte fest, dass ich constdie geerbte Methode vergessen hatte und das Klirren die ganze Zeit richtig war. Vertrauen Sie im Zweifelsfall dem Compiler. Wenn Sie am Compiler zweifeln, vertrauen Sie dem Compiler. :) +1 für beide, die mir beide geben, was ich gesucht und gebraucht habe!
Nevelis
17

Warnung bedeutet, dass im Bereich der abgeleiteten Klasse keine void * get (char * e) -Funktion vorhanden ist, da diese von einer anderen Methode mit demselben Namen ausgeblendet wird. Der Compiler sucht nicht nach Funktionen in Basisklassen, wenn die abgeleitete Klasse mindestens eine Methode mit dem angegebenen Namen hat, selbst wenn sie andere Argumente hat.

Dieser Beispielcode wird nicht kompiliert:

class A
{
public:
    virtual void Foo() {}
};

class B : public A
{
public:
    virtual void Foo(int a) {}
};


int main()
{
    B b;
    b.Foo();
    return 0;
}
Wurmloch-Assistent
quelle
1
Das ist ein guter Punkt: Das Verstecken geschieht tatsächlich aktiv , obwohl die unterschiedlichen Signaturen ausreichen sollten, um dies zu verhindern.
Jean-Denis Muys
Meine Definition von Verstecken hat die gleiche Signatur, aber nicht zu überschreiben ... was hier nicht der Fall ist.
NGauthier
Die Lösung, um ein Verstecken vor C ++ auf den Punkt zu bringen : "Fügen Sie eine using-Deklaration in die abgeleitete Klasse ein, wenn der Compiler die Funktionen der Basisklasse als Kandidaten betrachten soll", wie in anderen Antworten gezeigt.
Qris
Wenn es auch eine Lösung (mit ...) enthalten würde, sollte dies die akzeptierte Antwort sein, da dies die einzige ist, die richtig erklärt, was passiert und warum dies eine gültige Warnung ist
MikeMB
1
Dies ist die klarste Antwort, die ich denke. Es ist jedoch erwähnenswert, dass Sie tatsächlich noch anrufen können b.Foo();. Sie müssen nur schreiben b.A::Foo();.
Timmmm