Unklarheit: Implizite Typumwandlung im Konstruktor schlägt fehl
-
Hallo zusammen,
folgendes kleines Beispielprogramm lässt sich problemlos kompilieren. Die Struktur A bekommt im Konstruktor einen numerischen Wert übergeben, die Struktur B eine konstante Referenz auf eine Instanz von A. In der Rahmenfunktion main zeigt sich bei B(10); wie erwartet, dass implizit eine Instanz von A erzeugt wird. ...
struct A { A(int) {}; }; struct B { B(const A&) {}; }; int main() { A(10); B(10); }Wird nun der Typ (int) von dem Parameter des Konstruktors der ersten Struktur A durch eine konstante Referenz auf einen String (const std::string&) ersetzt und in der Hauptfunktion eine Zeichenkette übergeben, so lässt sich das Beispiel nicht mehr erfolgreich kompilieren:
// ... A(const string &) {}; // ... A("No one fucks with Tux ..."); B("... Linux the OS for the truly hardcore!");Übersetzungsvorgang schrieb:
# g++ main.cpp
main.cpp: In function 'int main()':
main.cpp:15: error: no matching function for call to 'B::B(const char [41])'
main.cpp:9: note: candidates are: B::B(const A&)
main.cpp:8: note: B::B(const B&)Kann mir jemand erklären, woran das liegt? - ich stehe im Moment ein wenig auf dem Schlauch.
Vermutung 1:
Der Konstruktor von basic_string ist als explicit deklariert. Vgl. basic_string.h.
explicit basic_string(const _Alloc& __a);Vermutung 2:
Liegt das einfach an der Tatsache, dass std::basic_string eine Klasse ist und int ein elementarer Typ? Wenn ja, wodurch lässt sich dieses Verhalten dann genau begründen?
Ich bin um jede Hilfe (gerne auch Vermutungen
dankbar
Grüße Martin
-
Hmm, ich kann Dir auf die primäre Frage keine Antwort geben, aber am explicit und daran, daß String eine Klasse ist liegt es nicht.
Folgendes geht nämlich:class C { public: explicit C(int a){}; }; class A { public: A(const C& c){ }; }; class B { public: B(const A& a) { }; }; int main() { A aa(C(10)); B bb(C(10)); return 0; };
-
Hmm, scheint am Konvertierungskosntruktor char* -> String zu liegen.
So geht es nämlich:
int main() { string test = "Dies ist ein Test"; A aa(test); B bb(test); return 0; };
-
Vielen Dank Tachyon, für die rasche Antwort. Ich glaube du hast mich ein wenig missverstanden.
Mir geht es um die implizite Typumwandlung. Wenn ich im obigen Beispiel B(A(10)); schreibe, so lässt sich auch dieses Programm ohne Probleme übersetzen.Nach einigen weiteren Versuchen bin ich zu folgendem Ergebnis gekommen: Es liegt an dem Modifizierer explicit.
struct A { A(int) {}; }; struct B { B(const A&) {}; }; int main() { A(10); B(10); }Dies kompiliert ohne Probleme, wo hingegen folgendes nicht funktioniert:
struct A { explicit A(int) {}; // hier ist das explicit dazugekommen }; struct B { B(const A&) {}; }; int main() { A(10); B(10); }# g++ main.cpp
main.cpp: In function 'int main()':
main.cpp:15: error: no matching function for call to 'B::B(int)'
main.cpp:9: note: candidates are: B::B(const A&)
main.cpp:8: note: B::B(const B&)Dennoch besten Dank für die Antwort und einen schönen Sonntag Abend.
Grüße Martin
-
Tachyon hat afaik Recht...
Im ersten Beispiel muss er nur eine implizite Umwandlung machen,
im zweiten Beispiel muss er zwei machen, was er nicht implizit kann...Gruß,
CSpille
-
Stimmt, ich habs mal wieder verpeilt. Eigentlich war die Frage klar...
-
der Aufruf
B("... Linux the OS for the truly hardcore!");würde zwei selbstdefinierte Konvertierungen erfordern, in einer Konvertierungssequenz ist aber immer höchtens eine selbstdefinierte Konvertierungsfunktion (Konstruktor oder operator) zulässig. Der Konstruktor von B benötigt ein A als Argument, A hat aber selbst keinen Konstruktor, der eine Zeichenkette erwartet. Erst die Umwandlung in einen String könnte zur Erstellung eines A führen, und das ist nicht erlaubt.
-
Es liegt daran, das es nur erlaubt ist eine Umwandlungsfunktion aufzurufen.
Bei deinem Beispiel siehts so aus:1. char* to string mit string(const char* )
2. string to A mit A(const string& ) // zuviel
3. A to B mit B(const A& )Edit: Musste lange überlegen, wie das nochmal war. Habe dazu mal irgendwo was gelesen. Wenn ich nur wüsste wo ?
-
Wunderbar, ich glaube nun habe ich verstanden.
Fazit:
Innerhalb eines Konstruktors darf nur _eine_ Umwandlung stattfinden. Ferner kann die Umwandlung durch setzen des Modifizierers "explicit" explizit unterbunden werden.Auf das obige Beispiel mit den numerischen Werten bezogen: Hier findet nur eine Typumwandlung statt, nämlich von int nach A. Desweiteren wurde die Umwandlung nicht durch explicit unterbunden, sodass das Programm einwandfrei kompiliert wird.
Wird explicit gesetzt, so lässt sich das Programm nicht kompilieren.Vgl. Beispiel mit Zeichenkette: Hier findet mehr als eine Typumwandlung statt. Zusätzlich ist der Konstruktor in basic_string.h als explicit gekennzeichnet. Beides trägt für unser Beispiel zum Scheitern des Übersetzungsvorgangs bei.
Stimmt das nun so?

Grüße Martin
-
lucky_tux schrieb:
Zusätzlich ist der Konstruktor in basic_string.h als explicit gekennzeichnet.
Das wäre allerdings tragisch. selbstverständlich dürfen wir
std::string foo = "bar";schreiben, da ist nichts explicit.
-
Stimmt. Lediglich der Konstruktor von basic_string, welchem eine Referenz auf einen Alocator übergeben wird, ist als extern deklariert. Da habe ich mich ein wenig verguckt.
