Operatorüberladung und Mehrdeutigkeiten...
-
Hallo alle zusammen,
ich hab mir eine kleine Klasse geschrieben, die als Puffer arbeiten soll.
Damit der Zugriff einfach möglich ist, hab ich ein paar Operatoren überladen.
Das hätte ich wohl besser gelassen
Am besten zeige ich jetzt erstmal was Quelltext...class TBuffer { public: //... alles was sonst noch so da rumdümpelt ... // Hier sind meine Problemfälle :) char& operator[](uint i); const char operator[](uint i) const; operator const char* () const; };
Die Klasse wollte ich so testen
TBuffer buffer1; buffer1.resize(1); buffer1[0] = '1';
Ärgerlicherweise kann der Compiler nicht erkennen, welcher Operator gemeint ist.
Er motzt mir an, das es mehrere Möglichkeiten gibt....\visual studio 2005\projects\buffertest\buffertest\buffertest.cpp(555) : error C2666: 'TBuffer::operator []': 3 Überladungen haben ähnliche Konvertierungen
...\visual studio 2005\projects\buffertest\buffertest\tbuffer.h(174): kann 'const char TBuffer::operator [](uint) const' sein
...\visual studio 2005\projects\buffertest\buffertest\tbuffer.h(173): oder "char &TBuffer::operator [](uint)"
oder "integrierter C++-Operator[(const char *, int)"
bei Anpassung der Argumentliste '(TBuffer, int)'Ursprünglich bin ich davon ausgegangen, das der Compiler feststellen kann, das in folgendem Code ein schreibender Zugriff stattfindet.
buffer1[0] = '1';
Leider scheint das nicht der Fall zu sein. Aber wie kann ich sowohl den Indexzugriff, als auch den Konvertierungsoperator zur Verfügung stellen?
Vielleicht kann mir ja einer von euch die Augen öffnen und mein Verständnis für die Operatoren was erweitern.Zum Schluss kann ich eigentlich nur schonmal Danke sagen.
NuckNuck
-
Erstmal erstaunt es mich, daß der Compiler den const-Operator mit in die Entscheidung einbezieht. Und dann könnte das Problem daran liegen, daß 0 kein uint ist und die beiden Typumwandlungen int->uint (für den zweiten Parameter) und TBuffer->const char* (für den ersten Parameter) dem Compiler gleich gut erscheinen.
Die STL-Lösung für dieses Dilemma siehst du bei std::string - der definiert die Umwandlung zum char* nicht als Konvertierungsoperator, sondern als Methode c_str(). Die Klasse CString (aus der MFC) löst das Problem, indem op[] einen int entgegennimmt (wodurch der eigene op[] ohne Typumwandlungen verwendet werden kann und "besser" ist als der vordefinierte Pointer-Operator).
-
Danke für die schnelle Antwort und dann auch noch eine so gute!
Du hattest recht, es war wirklich das Problem mit dem unsigned int. Dadurch wurden beide Operatoren mit in die Auswahl der passenden Operatoren aufgenommen.
Mit deiner Hilfe konnte ich das Problem schnell lösen. Ich musste einfach die beiden Index Operatoren zusätzlich mit einem int als Parameter anlegen.
Dann kann ich auch den Konvertierungsoperator weiterhin verwenden.Also CStoll, ich kann mich bei dir nur bedanken. Ist immer wieder schön, wenn mann eine gute Antwort bekommt. Hoffentlich bin ich bald fitt genug, damit ich auch mal Fragen beantworten kann
Bis zur nächsten Frage,
NuckNuck
-
CStoll schrieb:
Erstmal erstaunt es mich, daß der Compiler den const-Operator mit in die Entscheidung einbezieht.
Der Compiler zeigt die gesamte Kandidatenliste an, also alle Überladungen,für die passende Konvertierungen existieren. Das davon möglicherweise einige herausfallen, weil sie schlechter sind als diejenigen, die die Mehrdeutigkeit hervorrufen, ist erst einmal unerheblich. Zumal ja der Fehler tatsächlich in einer fehlerhaften Deklaration eben dieser Überladungen, die herausfallen würden, liegen könnte. Insofern ist die Nennung aller Kandidaten absolut notwendig.
Und dann könnte das Problem daran liegen, daß 0 kein uint ist und die beiden Typumwandlungen int->uint (für den zweiten Parameter) und TBuffer->const char* (für den ersten Parameter) dem Compiler gleich gut erscheinen.
Das ist korrekt. Genaugenommen ist die Signatur des eingebauten Operators (mit Pointer vor der Klammer)
operator[](T*,std::ptrdiff_t)
wobei ptrdiff_t (aus <cstddef>) implementationsabhängig ist.
Die Mehrdeutigkeit darf nicht überraschen: eine Überladung ist für ein gegebenes Tupel an Argumenten genau dann besser als eine andere Überladung, wenn der Rang der Konvertierungssequenz für keinen Parameter schlechter aber für wenigstens einen Parameter besser als derjenige für die andere Überladung ist. Oder wenn alle Konvertierungen bis auf const-Qualifikation gleich sind und eine Überladung zusätzlich const-Qualifikation erfordert, dann ist die andere Überladung besser.Das ist nur ein Aspeckt. Ein weiteres Problem ist, das durch den Konvertierungsoperator automatisch alle Operationen auf diesem Typen als Operatoren der Klasse auftreten, obwohl sie für diese nicht definiert wurden. ein Konvertierungsoperator in einen char pointer importiert also implizit
+ - unäres* == != < <= > >= [] ! (explizite Konvertierung in integrale Typen und implizit in void* oder bool: && || ?:)
Es ist enorm aufwändig alle diese Varianten durch geschickte Überladung zu eliminieren - und dieser Aufwand dürfte jeden vermeintlichen Gewinn durch den Konvertierungsoperator bei weitem übersteigen.nucknuck schrieb:
Danke für die schnelle Antwort und dann auch noch eine so gute!
Du hattest recht, es war wirklich das Problem mit dem unsigned int. Dadurch wurden beide Operatoren mit in die Auswahl der passenden Operatoren aufgenommen.
Mit deiner Hilfe konnte ich das Problem schnell lösen. Ich musste einfach die beiden Index Operatoren zusätzlich mit einem int als Parameter anlegen.
Dann kann ich auch den Konvertierungsoperator weiterhin verwenden.Also CStoll, ich kann mich bei dir nur bedanken. Ist immer wieder schön, wenn mann eine gute Antwort bekommt. Hoffentlich bin ich bald fitt genug, damit ich auch mal Fragen beantworten kann
Bis zur nächsten Frage,
NuckNuckEs ist eher unwahrscheinlich, dass das Problem damit endgültig gelöst ist.
-
camper schrieb:
CStoll schrieb:
Erstmal erstaunt es mich, daß der Compiler den const-Operator mit in die Entscheidung einbezieht.
...
Hmmm, wann wird denn der const-operator gezogen ?
Ich habe jetzt schon ein wenig rumgebastelt, aber wenn er beide hat, zieht er in meinem Beispielen immer den "un-const-en"....Gruß,
Simon2.
-
Simon2 schrieb:
Hmmm, wann wird denn der const-operator gezogen ?
Das ist dann der Fall, wenn dein Objekt vom Typ const TBuffer ist. Also entweder von vornherein als konstantes Objekt angelegt wurde oder - das ist der typische Fall - über eine Referenz auf const auf das Objekt verwiesen wird.
-
camper schrieb:
Simon2 schrieb:
Hmmm, wann wird denn der const-operator gezogen ?
Das ist dann der Fall, wenn dein Objekt vom Typ const TBuffer ist. Also entweder von vornherein als konstantes Objekt angelegt wurde oder - das ist der typische Fall - über eine Referenz auf const auf das Objekt verwiesen wird.
Ahaaa !!!
Irgendwie hatte ich immer mehr "das vordere const" (auf den Returnwert bezogen) im Blick.
Für die Wahl zählt aber (ist auch irgendwie logischer) "das hintere const" (auf den operator[]() bezogen) (Dass es dann auch der const-Returnwert gefordert ist, ist auch klar).Danke,
Simon2.