Seite 1 von 1

Visual C++: Operatorüberladung mit Namensräumen

Verfasst: So Okt 18, 2009 10:58 am
von Xin
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:

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;
  }
Es gibt eine globale Überladung, um Strings zu vergleichen:

Code: Alles auswählen

inline bool operator == ( XSD::String::String const & lhs, XSD::String::String const & rhs )
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:

Code: Alles auswählen

          if( identifier == temp->NodeIdentifier::GetIdentifier() ) )
Gleiches Ergebnis, auch die Langform hilft nicht weiter, obwohl hier defintiv und ohne Zweifel zwei Strings verglichen werden :

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 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:

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
        ]
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:

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;
    }
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 ^^)

Re: Visual C++: Operatorüberladung mit Namensräumen

Verfasst: So Okt 18, 2009 11:38 am
von Xin
Gar nicht so spaßig, Visual C++ den Spaß beizubiegen. Namespaces sind ja eine gute Erfindung, aber irgendwie ist die Unterstützung ebendieser etwas zu kurz gekommen. :-/

Code: Alles auswählen

namespace XSD {
namespace Util {
  class TagType;
}}

bool operator == ( XSD::Util::TagType lhs, XSD::Util::TagType rhs );
bool operator != ( XSD::Util::TagType lhs, XSD::Util::TagType rhs );

namespace XSD {
namespace Util {

class TagType
{
  friend bool ::operator ==( ::XSD::Util::TagType lhs, ::XSD::Util::TagType rhs);
  friend bool ::operator !=( ::XSD::Util::TagType lhs, ::XSD::Util::TagType rhs);

  ...
}

}}

inline bool operator == ( XSD::Util::TagType lhs, XSD::Util::TagType rhs ) { ... }
inline bool operator != ( XSD::Util::TagType lhs, XSD::Util::TagType rhs )  { ... }

Re: Visual C++: Operatorüberladung mit Namensräumen

Verfasst: So Okt 18, 2009 4:15 pm
von Kerli
Xin hat geschrieben:Ich finde heraus, dass Visual C++ die Strings als Bools vergleicht,
Für solche Fälle entstand das Safe Bool Idiom [1][2]. In C++0x wird das aber Gott sei dank nicht mehr notwendig sein, da dann das Schlüsselwort 'explicit' auch auf Castoperatoren ausgeweitet wird.

[1] http://en.wikibooks.org/wiki/More_C%2B% ... /Safe_bool
[2] http://www.artima.com/cppsource/safebool.html