Visual C++: Operatorüberladung mit Namensräumen
Verfasst: So Okt 18, 2009 10:58 am
Zum ersten muss ich sagen, dass sich der Visual C++ Compiler semantisch eigentlich mehr oder mehr minder korrekt verhält, aber das hilft mir nicht, wenn die Fehlermeldung mich darauf nicht aufmerksam macht. Insbesondere ist es nicht hilfreich, wie sich VC++ hier verhält.
Folgender Code, ich suche ein Element in einer Liste, das einen bestimmten Namen trägt:
Es gibt eine globale Überladung, um Strings zu vergleichen:
Die Überladung ist durchgehend ohne Probleme im Einsatz.
Trotzdem ist die Bedingung if( identifier == temp->NodeIdentifier::GetIdentifier() ) ) immer wahr, ich finde also immer das erste Element oder keins.
Um sicher zu stellen, dass hier wirklich Strings verglichen werden, schließlich gibt es einen eindeutigen Operator dafür, sorge ich dafür, dass es keine falsche Überladung von GetIdentifier() geben darf:
Gleiches Ergebnis, auch die Langform hilft nicht weiter, obwohl hier defintiv und ohne Zweifel zwei Strings verglichen werden :
Ich finde heraus, dass Visual C++ die Strings als Bools vergleicht, heißt: zuerst werden beide Strings implizit in Bools konvertiert, die - da sie beide Text enthalten, beide wahr sind. wahr == wahr, also ist das ganze immer wahr.
Ich nehme also die den operator bool() für die Strings wieder raus und erhalte folgende Fehlermeldung:
Mein operator == für Strings steht überhaupt nicht zu Diskussion, wird aber definitiv über ein Include eingebunden.
Visual C++ blendet Operatoren in höher gelegenen Namensräumen aus. Also findet er keinen und beginnt implizit und ohne Warning zum Erstbesten zu konvertieren, was ihm einfällt: bool, obwohl ein Operator existiert, dessen Signatur 100%ig passt - nur halt nicht im aktuellen Namensraum.
Ausgeblendet wurden die globalen 'operator ==', weil ich versehentlich die Operatoren für TagType und TagSave nicht global gesetzt habe.
Da man bei einen Operator in Kurzschreibweise selbst nicht wählen kann, aus welchem Namensraum er kommen soll ('if( identifier ::== rhs )'), muss man die lange Schreibweise wählen:
Das lässt sich nun wieder kompilieren und funktioniert auch unter Visual C++.
Um den Operator == in den jeweiligen Namensraum zu holen, wäre ein using operator ==; erforderlich gewesen. Sogesehen macht der Visual C++ hier alles richtig. Hilfreich ist es aber dennoch nicht, zumindest eine Warning 'Ich weiß, was Du diesen Sommer nicht tun wolltest' wäre nicht ganz schlecht gewesen. Auch die kommentarlose, implizite Konvertierung von gleich zwei Parametern halte ich für leicht fragwürdig.
Die letzte Version funktioniert jetzt, wie gewünscht. Ich baue jetzt die beiden versehentlich nicht globalen operatoren um, so dass sie global und damit gleichberechtigt stehen.
Und die Moral aus der G'schicht: operatoren in Namensräumen nutzt man nicht... (das steht glaube ich auch bei Scott Meyers, aber war ja auch hier nicht beabsichtigt ^^)
Folgender Code, ich suche ein Element in einer Liste, das einen bestimmten Namen trägt:
Code: Alles auswählen
T * Find( String const & identifier ) const
{
if( identifier.IsNotNull() )
for( T * temp = static_cast<T *>( List<T>::First ); temp; temp = (T*) temp->BaseNode<T>::Succ )
if( identifier == temp->GetIdentifier() ) )
return temp;
return NULL;
}
Code: Alles auswählen
inline bool operator == ( XSD::String::String const & lhs, XSD::String::String const & rhs )
Trotzdem ist die Bedingung if( identifier == temp->NodeIdentifier::GetIdentifier() ) ) immer wahr, ich finde also immer das erste Element oder keins.
Um sicher zu stellen, dass hier wirklich Strings verglichen werden, schließlich gibt es einen eindeutigen Operator dafür, sorge ich dafür, dass es keine falsche Überladung von GetIdentifier() geben darf:
Code: Alles auswählen
if( identifier == temp->NodeIdentifier::GetIdentifier() ) )
Code: Alles auswählen
T * Find( String const & identifier ) const
{
if( identifier.IsNotNull() )
for( T * temp = static_cast<T *>( List<T>::First ); temp; temp = (T*) temp->BaseNode<T>::Succ )
{
String const & rhs = temp->NodeIdentifier::GetIdentifier();
if( identifier == rhs ) )
return temp;
}
return NULL;
}
Ich nehme also die den operator bool() für die Strings wieder raus und erhalte folgende Fehlermeldung:
Code: Alles auswählen
c:\users\xin\desktop\genesys\trunk\de\xsd\util\list.h(548) : error C2665: "XSD::Util::operator ==": Durch keine der 2 Überladungen konnten alle Argumenttypen konvertiert werden.
c:\users\xin\desktop\genesys\trunk\de\xsd\util\tagged.h(13): kann 'bool XSD::Util::operator ==(XSD::Util::TagType,XSD::Util::TagType)' sein
c:\users\xin\desktop\genesys\trunk\de\xsd\util\tagged.h(55): oder "bool XSD::Util::operator ==(XSD::Util::TagSave,XSD::Util::TagSave)"
bei Anpassung der Argumentliste '(const XSD::String::String, const XSD::String::String)'
c:\users\xin\desktop\genesys\trunk\de\xsd\util\list.h(542): Bei der Kompilierung der Klassen-template der XSD::Util::XMLBaseNode *XSD::Util::NamedList<T>::Find(const XSD::String::String &) const-Memberfunktion
with
[
T=XSD::Util::XMLBaseNode
]
c:\users\xin\desktop\genesys\trunk\de\xsd\util\xml.h(125): Siehe Verweis auf die Instanziierung der gerade kompilierten Klassen-template "XSD::Util::NamedList<T>".
with
[
T=XSD::Util::XMLBaseNode
]
Visual C++ blendet Operatoren in höher gelegenen Namensräumen aus. Also findet er keinen und beginnt implizit und ohne Warning zum Erstbesten zu konvertieren, was ihm einfällt: bool, obwohl ein Operator existiert, dessen Signatur 100%ig passt - nur halt nicht im aktuellen Namensraum.
Ausgeblendet wurden die globalen 'operator ==', weil ich versehentlich die Operatoren für TagType und TagSave nicht global gesetzt habe.
Da man bei einen Operator in Kurzschreibweise selbst nicht wählen kann, aus welchem Namensraum er kommen soll ('if( identifier ::== rhs )'), muss man die lange Schreibweise wählen:
Code: Alles auswählen
T * Find( String const & identifier ) const
{
if( identifier.IsNotNull() )
for( T * temp = static_cast<T *>( List<T>::First ); temp; temp = (T*) temp->BaseNode<T>::Succ )
{
String const & rhs = temp->NodeIdentifier::GetIdentifier();
if( ::operator ==( identifier, rhs ) )
return temp;
}
return NULL;
}
Um den Operator == in den jeweiligen Namensraum zu holen, wäre ein using operator ==; erforderlich gewesen. Sogesehen macht der Visual C++ hier alles richtig. Hilfreich ist es aber dennoch nicht, zumindest eine Warning 'Ich weiß, was Du diesen Sommer nicht tun wolltest' wäre nicht ganz schlecht gewesen. Auch die kommentarlose, implizite Konvertierung von gleich zwei Parametern halte ich für leicht fragwürdig.
Die letzte Version funktioniert jetzt, wie gewünscht. Ich baue jetzt die beiden versehentlich nicht globalen operatoren um, so dass sie global und damit gleichberechtigt stehen.
Und die Moral aus der G'schicht: operatoren in Namensräumen nutzt man nicht... (das steht glaube ich auch bei Scott Meyers, aber war ja auch hier nicht beabsichtigt ^^)