std::string & +-Operator



  • Hallo zusammen!

    Ich hab gerade mit Entsetzen festgestellt, dass der Additionsoperator des std::string nur für strings und Char-Arrays in der Parameterliste implementiert ist. Ich konnte jedenfalls z.B. folgende Zeile nicht kompilieren:

    string myString="hallo";
    cout<<(myString+100).c_str()<<flush;
    

    Was spricht denn gegen eine Implementierung des Additionsoperators auf folgende Art und Weise?

    template<class T> string operator +(const T val)
      {
         //Bem.: für die Addition von strings existiert die Implementierung ja
         return((*this)+(ostringsteam()<<val).str()); 
      }
    

    Warum ist denn sowas nicht im string implementiert?
    (Wenn man Java kennt, geht es einem recht bald auf die Nerven ständig manuell einen stringstream zu verwenden um anschließend die beiden strings zu verketten)

    Danke!

    Grüße,
    TS++



  • Was dagegen spricht? Das liegt daran, dass std::string "nur" ein Container ist. In der STL gibt es verschiedene Arten von Containern, z.B. vector, deque, set, map, usw. Alle Containern haben als Schnittmenge bestimmte Eigenschaften, wie z.B. einen Iterator-Typ, die Methoden begin() und end() und noch einiges mehr. Damit ist es möglich, bestimmte generische Templatefunktionen zu definieren, z.B. std::find!
    std::string macht da auch keine Ausnahmne, nur dass dieser Container über die gemeinsame Schnittmenge hinaus Methoden und Operatoren implementiert hat, so dass sich dieser Container wie ein String behandeln lässt. Dadurch könntest Du Dir auch einen String erstellen, dessen Elemente nicht vom Typ char sind, sondern Zeiger eines bestimmten Typs, oder was auch immer. Bei vector, etc. gibt man diesen Typ als Templateparameter an. Im Fall eines Stingcontainers im Prinzip auch, nur heisst die Templateklasse nicht std::string, sondern in Wirklichkeit std::basic_string.
    std::string ist schliesslich nur von std::basic_string abgeleitet, wobei als Template-Argument "char" eingesetzt wurde (plus entspr. char_traits). Deshalb kann basic_string nur schlecht einen solchen Operator implementieren, wie Du ihn gerne hättest, denn wie sollte er sich verhalten, wenn basic_string nicht char's sondern void-Pointer speichern würde?
    Vielleicht hätte man bei der abgeleiteten Klasse std::string einige Operatoren noch mal explizit überladen können, aber das ist nun mal nicht der Fall 😞

    Vielleicht ist Dir nun auch klar (falls Du Dich das auch schon gefragt hast), warum ein string auch nicht die Methoden "ToUpperCase" und "ToLowerCase" kennt. Theoretisch hätte man dies auch noch alles in basic_string reinpacken können. Aber dann hätte man auch den Template-Struct std::char_traits<class ZTyp> um erheblich mehr Methoden erweitern müssen.

    Gruß,
    wischmop



  • Danke für deine ausführliche Antwort!
    Wie überbrückst du denn dieses peinliche Manko?
    Denn wie ich schon gesagt habe, Stringoperationen sollten doch eigentlich so leicht wie nur irgendwie möglich abhandelbar sein (siehe Java).
    => Wenn ich zum Beispiel std::string mit einer eigenen Stringklasse einkapsle,
    löse ich mich ja von der Klassenhierarchie der STL und ich könnte problemlos den +-Operator überladen.

    Es muss doch einen bequemeren Ansatz geben, als der der aktuell noch durch die STL geboten wird.

    Grüße,
    TS++

    [ Dieser Beitrag wurde am 03.07.2003 um 11:10 Uhr von TS++ editiert. ]



  • Oder wenn ich nochmal eine eigene Stringklasse von std::string ableite und dort den Additionsoperator überlade. Müsste doch theoretisch auch funktionieren. Ich muss mich doch von der Klassenhierarchie der STL gar nicht entfernen, wenn ich's mir genau überlege. Die bereits implementierten Operatoren bleiben ja erhalten. Oder seh ich das noch zu positiv? 😉

    Grüße,
    TS++



  • Es muss doch einen bequemeren Ansatz geben

    Ansatz 1: boost::lexical_cast
    Ansatz 2: Selber op+ implementieren:

    #include <string>
    #include <sstream>
    #include <iostream>
    using namespace std;
    template <class Char, class Trait, class Alloc, class T>
    basic_string<Char, Trait, Alloc> operator+(const basic_string<Char, Trait, Alloc>& lhs, T t)
    {
        ostringstream str;
        str << t;
        return lhs + str.str();
    }
    template <class Char, class Trait, class Alloc, class T>
    basic_string<Char, Trait, Alloc> operator+(T t, const basic_string<Char, Trait, Alloc>& rhs)
    {
        ostringstream str;
        str << t;
        return str.str() + rhs;
    }
    
    int main()
    {
        string s("Hallo");  
        cout << (s + 5) << endl;
        cout << (5 + s) << endl;
        cout << (s + "Hallo") << endl;
        cout << ("Welt" + s) << endl;
        return 0;
    }
    

    Wenn man die Template op+ nicht ganz so "greedy" haben will, sollte man allerdings function enabler benutzen. Das ist aber dann etwas komplizierter.



  • Danke, Hume! 😃

    Es ist für mich das erste mal, dass ich, ich weiss nicht wie ich das nennen soll, einen Operator "global" überlade. Funktionieren tut's. Nur hätt ich noch gern gewusst, ob so ein "global" überladener Operator evtl. bereits existierende
    überladene Operatoren überschreibt. D.h.: geht das "globale" Überladen vor "lokalem" Überladen?( z.B. in einer Klasse )
    Falls ja, so müsste ich ja in der Lage sein, alle Implementierungen in std::string selbst zu bestimmen, ohne die entsprechende Klasse zu modifizieren oder eine weitere Klasse abzuleiten.
    Bitte klär mich auf. Das ist wieder was Neues für mich!

    Danke!

    Grüße,
    TS++



  • Hab grad festgestellt, dass ein erneutes Überladen eines bereits in einer Klasse überladenen Operators nicht möglich ist. ("ambiguous overload")
    Aber das ist ja beim String-Beispiel eh nicht der Fall!

    Grüße,
    TS++



  • Nur hätt ich noch gern gewusst, ob so ein "global" überladener Operator evtl. bereits existierende
    überladene Operatoren überschreibt.

    Er nimmt natürlich an der Namensauflösung teil. Damit kann er unter Umständen andere Operatoren verdecken.

    geht das "globale" Überladen vor "lokalem" Überladen

    Nein. Die beiden sind gleichberechtigt. Aber dadurch kann ein globaler op+ natürlich einen Member-Op+ verdecken.
    Bsp:

    class A
    {
    public:
    A operator+(long) {return *this;}
    };
    
    int main()
    {
    A a;
    a = a + 4; // ruft A::operator+ auf. 4 ist int. int konvertierbar nach long
    }
    

    Kommt jetzt:

    template <class T>
    A operator + (A a1, T i)
    {
    return a1;
    }
    

    hinzu, haben wir:

    a = a + 4; // ruft ::operator+ auf. 4 ist int -> T = int -> perfect match
    

    Falls ja, so müsste ich ja in der Lage sein, alle Implementierungen in std::string selbst zu bestimmen, ohne die entsprechende Klasse zu modifizieren oder eine weitere Klasse abzuleiten.

    Verstehe ich nicht.



  • Danke, Hume!

    Falls ja, so müsste ich ja in der Lage sein, alle Implementierungen in std::string selbst zu bestimmen, ohne die entsprechende Klasse zu modifizieren oder eine weitere Klasse abzuleiten.

    Verstehe ich nicht.

    Vielleicht hab ich mich ja etwas unklar ausgedrückt. Was ich meinte, war folgendes:
    Falls ich tatsächlich in der Lage gewesen wäre, bereits überladene Operatoren, wie z.B. für die Addition von std::string und const char*, nochmals(global) zu überladen, dann wäre es mir ja möglich gewesen, die bereits implementierten Routinen zur Addition von string und char* und von string und string nochmals selbst zu schreiben. Ich hätte also über den global überladenen Operator alle bereits implementierten Operatoren in string überladen. Aber das geht ja offensichtlich nicht( "ambiguous overloading" )!
    Würde bereits ein Template-Operator + im String existieren, so würde der Lösungsansatz mit globalem Überladen ja auch nicht funktionieren. (Hab's einfach mal ausprobiert)

    Grüße,
    TS++


Anmelden zum Antworten