Problem mit Function-Overloading einer methode



  • Hallo;

    ich habe ein Verständnisproblem mit dem Überladen einer Methode.

    #include <string>
    
    class beispiel1
    {
    public:
    	std::string func(const bool flag = false) const {return "";}
    	void func(const std::string s) {}
    
    	void test() {func("");}
    };
    

    hier zeigt mir Visual Studio 2010 bei func(""); an, dass es 2 Überladungen gibt.

    Wenn ich das const bei der oberen Methode entferne, dann verschwindet der Fehler!

    #include <string>
    
    class beispiel2
    {
    public:
    	std::string func(const bool flag = false) {return "";}
    	void func(const std::string s) {}
    
    	void test() {func("");}
    };
    

    Umgehen kann ich den Fehler wie folgt:

    #include <string>
    
    class beispiel3
    {
    public:
    	std::string func(const bool flag = false) const {return "";}
    	void func(const std::string s) {}
    
    	void test() {func(std::string(""));}
    };
    

    Kann mir jemand erklären, warum im Falle einer const-Methode der Aufruf auf die erste Methode passt und bei einer nicht-const Methode nicht.

    Ich brauche leider const-Methoden, da es sich im Realprojekt um Getter/Setter handelt.

    Gruss Ludger.



  • Das liegt an den möglichen impliziten Konvertierungen:

    "" ist ein C-String, d.h. vom typ char*, d.h. ein Pointer und als solche implizit in ein bool umwandelbar. Damit hat der Compiler recht, es ist nicht zu entscheiden, ob du den leeren C-String nun implizit in einen std::string verwandeln lassen willst oder in einen bool.

    Der Grund, warum die erste Version zweideutig ist und die zweite nicht ist, dass die erste Version folgende Konvertierungen benötigt:

    1. Konvertierung char* -> bool
    2. Konvertierung beispiel1* -> beispiel1 const* (den this-Pointer)

    die zweite Version aber die folgenden Konvertierungen:

    1. char* -> char const*
    2. char const* -> std::string

    Damit sind für beide Parameter"anpassungen" zwei Konvertierungen nötig und es gibt keinen "besseren" Kandidaten.

    Bei deinem zweiten Versuch fällt die Wahl auf die bool-Version, wei die const-Konvertierung des this-Pointers entfällt. Das dürfte allerdings nicht sein was du möchtest/erwartest.



  • Hallo,

    danke pumuckl! Das es an impliziten Konvertierungen liegt, hatte ich schon vermutet.

    Also bleibt mir nur jedesmal wenn ich einen String übergebe, diesen als std::string("bla bla") zu übergeben. Nicht schön, aber auch kein Beinbruch.

    Gruss Ludger.



  • Oder Du machst dir eben noch eine

    void func(const char * s) {/*...*/}
    

    Wenn Du eh schon am Überladen bist.


  • Mod

    Bezogen auf einen bestimmten Funktionsaufruf ist eine Überladung F ist besser als eine eine G dann, wenn die notwendige implizite Konvertierung eines jeden Arguments x für den Aufruf von F nicht schlechter ist als die korrespondierende implizite Konvertierung von x um G auszurufen (dann ist F nicht schlechter als G), und für mindestens ein Argument die implizite Konvertierung für den Aufruf von F besser ist als die korrespondierende implizite Konvertierung um G aufzurufen.
    Es gibt noch zusätzliche Regeln, um in manchen Fällen, in denen F nach obiger Regel weder besser noch schlechter als G wäre, eine Entscheidung herbeizuführen; die sind allerdings in deinen Beispielen nicht relevant.

    Nehmen wir also die Beispiele auseinander: (1)

    std::string func(const bool flag = false) const {return "";}
    	void func(const std::string s) {}
    
    	void test() {func("");}
    

    Memberfunktionen haben immer einen zusätzlichen impliziten Objektparameter, der das Objekt repräsentiert, für dass die Funktion aufgerufen wird (innerhalb der Funktion ist dass dann *this (bei statischen Memberfunktionen wird er für Überladunsgauflösung aber (effektiv) nicht herangezogen)).
    Für Zwecke der Überladungsauflösung tut man dabei so, als ob der Objektparameter den Typ cv foo& hätte (foo sei die betreffende Klasse und cv bezeichnet die const/volatile-Qualifikation der Funktion) oder - in C++11 - den Type cv foo&& wenn die Funktionen einen &&-ref-Qualifizierer hat - wobei diese fiktive Referenz immer direkt an das Argument gebunden wird, es werden keine temporären Objekte dafür erzeugt.

    In diesem Beispiel muss für den Aufruf der Überladung

    std::string func(const bool flag = false) const {return "";}
    

    der Objektparameter (implizit als *this gegeben) eine zusätzliche const-Qualifikation bekommen, für das eigentliche Funktionsargument genügt einen boolean-Konvertierung.
    Für die zweite Überladung hingegen muss das Funktionsargument nicht angepasst werden, diese Überladung ist demzufolge bzgl. dieses Parameters besser als die andere, während für das eigentliche Funktionsargument ein Konstruktor aufgerufen werden muss, diesbezgl.ist die Überladung daher schlechter als die Andere.
    Im Ergebnis ist keine Überladung besser als die andere, folglich ist der Aufruf mehrdeutig.

    Variante 2:

    std::string func(const bool flag = false) {return "";}
    	void func(const std::string s) {}
    
    	void test() {func("");}
    

    Hier besteht keine Unterschied bzgl. des Objektparameters für die beiden Überladungen, die erste Überladung ist aber besser als die zweite in Hinsicht auf das Funktionsargument - es wird somit die erste Überladung aufgerufen.

    3. Beispiel

    std::string func(const bool flag = false) const {return "";}
    	void func(const std::string s) {}
    
    	void test() {func(std::string(""));}
    

    Ein std::string kann nicht implizit in bool umgewandelt werden, die erste Überladung ist also von vornherein kein Kandidat.



  • el hep schrieb:

    Also bleibt mir nur jedesmal wenn ich einen String übergebe, diesen als std::string("bla bla") zu übergeben. Nicht schön, aber auch kein Beinbruch.

    Alternativ kannst du noch ein func(const char* const) bereitstellen, das lediglich die string-Variante aufruft.

    Edit: Mein Gott, man sollte keine Tabs aus der letzten Session unaktualisiert lassen...



  • Hallo,

    @camper Das war sehr ausführlich und verständlich! Danke!

    Ich werde doch eine weitere Variante mit

    void func(const char const* s) {}
    

    zur Verfügung stellen.

    Gruss Ludger.


Anmelden zum Antworten