Nutzung der STL in Firmen. Ist die STL schlimm?



  • RHBaum: std::string ist oftmals wirklich nicht ideal. Auch ist es äusserst unglücklich, dass wir für Algorithmen wie split Boost brauchen und sowas nicht in der Standardbibliothek haben.

    Qt (nicht QT) ist aber auch ein sehr bekanntes Framework. In dessen Kontext auch Qt-Container zu verwenden finde ich ok (man hat ja kaum eine andere Wahl), in diesem Thread gehts aber eher um selbstgebastelte Lösungen und das Not-Invented-Here-Syndrom.

    Was unnötige Kopien angeht, die sind mit C++11 Move-Semantik weitgehend beseitigt. Je nach Anwendungsfall kann man auch mit Techniken wie Expression Templates recht tricksen.


  • Mod

    Wenn man aber eine Qt App entwickelt, dann kann man das sehr wohl erwarten und dann passen die Qt Container deutlich besser in die App als die STL.

    Da hast du voellig Recht. Daher auch meine Nachfrage: Wenn man die Qt-Container einfach ohne praktischen Grund verwendet (einer waere zum Beispiel dass Qt-GUI diese benoetigt), einfach weil man sie besser kennt, dann ist das sehr merkwuerdig.

    @hustbaer: Je mehr ich ueber deine Arbeit hoere, desto mehr tust du mir Leid. Ist furchtbar.


  • Mod

    die sind mit C++11 Move-Semantik weitgehend beseitigt.

    Weitgehend?



  • STLOderWas schrieb:

    Hallo,

    ich hatte in letzter Zeit mehrere Vorstellungsgespraeche bei Softwarefirmen (bunt gemixt, Spielefirmen, etc.).

    In den Gesprachen habe ich immer gesagt ich versuche wo es nur geht die STL und Boost zu benutzen. Fast jedes mal bekam ich die Antwort, "STL/Boost benutzen wir nicht, wir haben unsere eigenen Container usw.".

    Wenn ich das hoere laeuft es mir immer kalt den Rucken runter.

    Wie ist eure Meinung dazu?

    Ich würde mich fragen, warum es in den Unternehmen andersläuft, mir die Programmierpraktiken/Bibs genauer anschauen wollen. Mit genauere Kenntnis der Zusammenhänge fiele das Argumentieren für oder gegen bestimmte Arbeitsweisen leichter.
    Da ich auch (wirtschaftlich) rechnen kann, könnte ich Vor- und Nachteile direkt vorrechnen.
    (Außerdem, steh ich auf Optimieren, Refactoring und so *sabber* wenn es zufällig irgendwas schluderig zusammengefrickeltes gibt, dann nichts wie hin zum Grusel-Gedöns...;)



  • Arcoth schrieb:

    die sind mit C++11 Move-Semantik weitgehend beseitigt.

    Weitgehend?

    Ja, Move-Semantik alleine löst nicht alle Probleme.

    std::string t = "text";
    std::string r = t + " und " + "mehr " + t;
    

    Wie viele temporäre Objekte hast du hier? Und wie viele sind tatsächlich nötig? QString scheint diesbezüglich intelligenter zu sein.



  • Nexus schrieb:

    Arcoth schrieb:

    die sind mit C++11 Move-Semantik weitgehend beseitigt.

    Weitgehend?

    Ja, Move-Semantik alleine löst nicht alle Probleme.

    std::string t = "text";
    std::string r = t + " und " + "mehr " + t;
    

    Wie viele temporäre Objekte hast du hier? Und wie viele sind tatsächlich nötig? QString scheint diesbezüglich intelligenter zu sein.

    Könntest Du das Problem so erläutern, dass auch nicht-C++-Experten das verstehen? Da wäre ich Dir dankbar.



  • Arcoth schrieb:

    @hustbaer: Je mehr ich ueber deine Arbeit hoere, desto mehr tust du mir Leid. Ist furchtbar.

    Was ist furchtbar?
    Dass wir kein Qt verwenden?
    OMFG... ja, ganz schlimm 🙄



  • ich bins schrieb:

    Könntest Du das Problem so erläutern, dass auch nicht-C++-Experten das verstehen? Da wäre ich Dir dankbar.

    Das Problem hierbei ist, dass eine klassische Implementierung für jeden Aufruf von operator+ ein neues temporäres Objekt erzeugt. Die schrittweise Auswertung könnte also so aussehen:

    r = a + b + c + d;
    r =   ab  + c + d;
    r =      abc  + d;
    r =       abcd;
    

    Dadurch wird der String in a und b z.B. 3 Mal kopiert (er kommt nämlich in ab , abc und abcd vor), der in c 2 Mal ( abc , abcd ), etc. Bei der letzten Zuweisung kann der Move-Zuweisungsoperator benutzt werden.

    Wenn du hingegen

    r = a;
    r += b;
    r += c;
    r += d;
    

    schreibst, arbeitest du direkt in-place und kannst die temporären Wegwerfobjekte vermeiden. Das ganze ist immer noch nicht optimal, da der std::string unter Umständen reallokiert werden muss, obwohl die Grösse aller beteiligten Objekte im Voraus addiert werden könnte.

    Eine mögliche Lösung des Problems stellen Expression Templates dar; diese auf Lazy Evaluation (später Auswertung) basierende Technik sorgt dafür, dass zwischenzeitliche Wegwerf-Objekte vermieden werden. operator+ würde dann keinen std::string zurückgeben, sondern ein Proxy-Objekt, welches zu std::string konvertierbar ist -- bei der Konvertierung wird der String zusammengesetzt. Zu diesem Zeitpunkt sind die notwendigen Informationen wie gesamte Grösse vorhanden und können zur Optimierung ausgenutzt werden.

    Expression Templates führen allerdings zu Problemen mit Templates und Typinferenz, da das Resultat selbst kein String ist. Folgender Code würde also zu unerwarteten Ergebnissen führen:

    template <typename T>
    void fn(const T& t);
    
    auto r = a + b + c + d; // r ist kein std::string
    fn(a + b + c + d);      // T ebenfalls nicht
    


  • RHBaum schrieb:

    Kopierst du denn andauernd Container durch die Gegend, oder wo verlässt du dich auf COW, wo es mit der STL nötig wäre "selbst aufzupassen"?

    Ja klar 🙂 wenn man sich auf COW verlaesst, muss man sich keine sorgen um die Kopien machen ... damit kann man viel einfacheren Code schreiben.

    http://www.gotw.ca/publications/optimizations.htm 😉

    Keine aktuelle std::string Implementierung wird COW verwenden, da dies spätestens seit C++11 afaik nichtmehr erlaubt ist. MSVC z.B. verwendet aus guten Gründen schon wesentlich länger kein COW mehr (stattdessen small string optimization)...

    RHBaum schrieb:

    berühmtes Besipiel:

    QString strCSV = "1834;28540;2345;2384;293045;12034;23095;92582";
    
    QStringList splitList = strCSV.split(';');
    

    jetzt programmier das mal mit der STL nach ....
    geht sicherlich, sieht aber nicht ganz so easy aus oder ?

    und dann, versuch mal das Splitten in eine komfortable funktion zu packen, man braucht das ja öfter, und garantiere dabei das das performance technisch keine unnötigen kopien macht. ohne precompiler Macros !
    Du wirst keine Lösung finden die sooo naiv, unschuldig wie die obige mit COW aussieht.

    Die einfache und direkte Lösung wäre wohl über std::getline(). Das erzeugt aber Kopien. Gänzlich ohne Kopien wird's tatsächlich schwierig. Wenn du mich fragst, übersiehst du hier aber sowieso etwas ganz grundlegendes, wenn du das effizient haben willst: Der Grund, wieso du hier splittest, wird wohl sein, dass du über die einzelnen Komponenten iterieren willst. Wofür also überhaupt erst eine Liste machen (was hilft dir COW, wenn du dann erst wieder für jeden String ein Listenelement allokierst)? Die effiziente Lösung wäre hier wohl, direkt über den Originalstring zu iterieren (auch gleich verpackt in einer netten Funktion):

    #include <algorithm>
    #include <iterator>
    #include <string>
    #include <vector>
    #include <iostream>
    
    namespace csv
    {
    	const std::locale& locale()
    	{
    		struct csv_facet : std::ctype<char>
    		{
    			static const mask* get_table()
    			{
    				static std::vector<mask> v(classic_table(), classic_table() + table_size);
    				v[';'] |= space;
    				return &v[0];
    			}
    
    			csv_facet(std::size_t refs = 0)
    				: ctype(get_table(), false, refs)
    			{
    			}
    		};
    
    		static const std::locale csv_loc(std::locale(), new csv_facet);
    
    		return csv_loc;
    	}
    
    	template <typename T, typename F>
    	void iterate_line(std::string str, F f)
    	{
    		struct memory_streambuf : public std::streambuf
    		{
    			memory_streambuf(char* begin, char* end)
    			{
    				setg(begin, begin, end);
    			}
    		};
    
    		memory_streambuf buffer(&str[0], &str[0] + str.length());
    		std::istream stream(&buffer);
    		stream.imbue(locale());
    
    		for (auto n = std::istream_iterator<T>(stream); n != std::istream_iterator<T>(); ++n)
    		{
    			f(*n);
    		}
    	}
    }
    
    int main()
    {
    	csv::iterate_line<int>("1834;28540;2345;2384;293045;12034;23095;92582", [](auto n){
    		std::cout << n << '\n';
    	});
    
    	return 0;
    }
    

    Hier wird absolut nichts kopiert und so im Vorbeigehen werden die Substrings auch noch gleich als int geparsed...

    RHBaum schrieb:

    Ziemlich oft gesehen:

    typedef QMap<QString,QString> DictionaryT; /// klar mit Hashmap geht noch bissi mehr, aber so schlecht iss das nicht
    

    Gute Idee:
    STL Verwenden !
    Schlechte Idee:

    typedef std::map<std::string,std::string> DictionaryT;
    

    Und sich dann wundern, warum sich das arbeiten mit dem PC anfühlt, als wär die Handbremse angezogen !
    Sowas macht keiner ?

    Huch!? was ist das Problem hier? Sowohl std::map als auch QMap basieren auf einem Suchbaum!? Im Falle der STL könnte man einfach std::unordered_map<std::string, std::string> verwenden und schon hat man eine Hashtable...

    Nexus schrieb:

    Auch ist es äusserst unglücklich, dass wir für Algorithmen wie split Boost brauchen und sowas nicht in der Standardbibliothek haben.

    Finde ich nicht. split() lässt sich, wie oben angeführt, aus Prinzip nicht effizient implementieren und ist auch völlig unnötig, da man in der Regel eigentlich keine Liste von Komponenten braucht, sondern lediglich über diese iterieren will. Und das lässt sich mit Hilfe von streams wesentlich besser umsetzen...

    hustbaer schrieb:

    Arcoth schrieb:

    @hustbaer: Je mehr ich ueber deine Arbeit hoere, desto mehr tust du mir Leid. Ist furchtbar.

    Was ist furchtbar?
    Dass wir kein Qt verwenden?
    OMFG... ja, ganz schlimm 🙄

    Ich fänds sogar eher furchtbar, wenn ich Qt verwenden müsste... 😉

    Nexus schrieb:

    Eine mögliche Lösung des Problems stellen Expression Templates dar; diese auf Lazy Evaluation (später Auswertung) basierende Technik sorgt dafür, dass zwischenzeitliche Wegwerf-Objekte vermieden werden. operator+ würde dann keinen std::string zurückgeben, sondern ein Proxy-Objekt, welches zu std::string konvertierbar ist -- bei der Konvertierung wird der String zusammengesetzt. Zu diesem Zeitpunkt sind die notwendigen Informationen wie gesamte Grösse vorhanden und können zur Optimierung ausgenutzt werden.

    Oder man verwendet einfach std::ostringstream... 😉



  • Die effiziente Lösung wäre hier wohl ...

    Keine Streams zu verwenden.

    [](auto n){ 
         });
    

    Ist das gueltiges C++11. Wenn nein, dann biste im Falschen Forum. 🙂



  • Die effiziente Lösung wäre hier wohl ...

    ganz auf die Variable

    QString strCSV = "1834;28540;2345;2384;293045;12034;23095;92582";
    

    zu verzichten und gleich splitList aus der Datei zu lesen.



  • dot schrieb:

    Finde ich nicht. split() lässt sich, wie oben angeführt, aus Prinzip nicht effizient implementieren

    Warum nicht? Wenn du den Kompromiss eingehen kannst, dass nicht jeder Teilstring seinen eigenen Speicher braucht und du nur lesend auf die Teile zugreifst, kannst du durchaus optimieren. Nämlich indem du "leichte" Strings benutzt (Ranges mit Anfangs- und Endzeiger). Du kannst das auch mit Iteration kombinieren, ähnlich wie std::string::find() mit einem Anfangs-Index bzw. -Iterator.

    dot schrieb:

    Und das lässt sich mit Hilfe von streams wesentlich besser umsetzen...

    Wie viele Zeilen Code brauchst du, um das gleiche mit Streams zu erreichen? Ausserdem sind Streams nicht gerade billig. Du brauchst mindestens einen Puffer für den Stream und einen für den String, in den du extrahierst -- zusätzlich zum String, aus dem du ausliest. Wenn du hingegen nur iterieren würdest, kämst du gänzlich ohne dynamische Speicheranforderung aus.

    Wenn du natürlich direkt aus der Datei liest, siehts wieder anders aus. Ich bin hier davon ausgegangen, dass ein String vorliegt.



  • Werner Salomon schrieb:

    Die effiziente Lösung wäre hier wohl ...

    ganz auf die Variable

    QString strCSV = "1834;28540;2345;2384;293045;12034;23095;92582";
    

    zu verzichten und gleich splitList aus der Datei zu lesen.

    Das stimmt natürlich, setzt aber voraus, dass man eine Datei hat... 😉

    knivil schrieb:

    Die effiziente Lösung wäre hier wohl ...

    Keine Streams zu verwenden.

    [](auto n){ 
         });
    

    Ist das gueltiges C++11. Wenn nein, dann biste im Falschen Forum. 🙂

    Es ist voraussichtlich gültiges C++14, MSVC kann's schon... :p

    Nexus schrieb:

    dot schrieb:

    Finde ich nicht. split() lässt sich, wie oben angeführt, aus Prinzip nicht effizient implementieren

    Warum nicht? Wenn du den Kompromiss eingehen kannst, dass nicht jeder Teilstring seinen eigenen Speicher braucht und du nur lesend auf die Teile zugreifst, kannst du durchaus optimieren. Nämlich indem du "leichte" Strings benutzt (Ranges mit Anfangs- und Endzeiger). Du kannst das auch mit Iteration kombinieren, ähnlich wie std::string::find() mit einem Anfangs-Index bzw. -Iterator.

    Das ist auch nicht optimal, da du den String effektiv öfter als genau einmal durchläufst, da du immer das Ende vom nächsten Teil suchen musst, bevor du das Intervall zurückgeben kannst, welches der Benutzercode in weiterer Folge wohl wieder durchlaufen wird. Außerdem musst du weiterhin irgendwie eine Liste von Intervallen zurückgeben, was zuallermindest eine dynamische Speicheranforderung bedeutet...

    Nexus schrieb:

    dot schrieb:

    Und das lässt sich mit Hilfe von streams wesentlich besser umsetzen...

    Wie viele Zeilen Code brauchst du, um das gleiche mit Streams zu erreichen?

    Siehe z.B. oben.

    Nexus schrieb:

    Ausserdem sind Streams nicht gerade billig. Du brauchst mindestens einen Puffer für den Stream und einen für den String, in den du extrahierst -- zusätzlich zum String, aus dem du ausliest.

    Custom streambuf 😉



  • Nexus schrieb:

    std::string t = "text";
    std::string r = t + " und " + "mehr " + t;
    

    Wie viele temporäre Objekte hast du hier? Und wie viele sind tatsächlich nötig? QString scheint diesbezüglich intelligenter zu sein.

    Eigentlich egal wie viele Temps es hier gibt. Das Beispiel hier lässt sich relativ gut mit Move optimieren. COW dagegen bringt hier mMn. nichts bzw. nicht viel.

    Das grundlegende Problem ist aber eher dass C++ keine Unterscheidung zwischen Strings und String-Builder macht.
    Und der String daher nie wissen kann ob er auf schnelle Änderungen optimieren soll (=deutlich zu viel Speicher anfordern wie es ein String-Builder machen würde) oder auf Speicherverbrauch.
    Weil er nie weiss wann er "fertig" ist.
    Und das lässt sich mit COW genau so wenig lösen wie ohne COW.

    Davon abgesehen... ich hab nichts gegen COW - wenns angebracht und sinnvoll umgesetzt ist finde ich es sogar sehr toll.
    Ich hab' nur was gegen COW-um-jeden-Preis.



  • schnelle Änderungen optimieren soll (=deutlich zu viel Speicher anfordern wie es ein String-Builder machen würde) oder auf Speicherverbrauch.

    Auch fuer std::string gibt es reserve, d.h. von aussen steuerbar. Und als std::vector<char> ist die Allokationsstrategie nicht die schlechteste, er braucht nicht wissen, wann er fertig ist.

    Das ist auch nicht optimal, da du den String effektiv öfter als genau einmal durchläufst ...

    Nein, zum Iterieren gibt es Iteratoren.

    Ansonsten: Bitte kein vagen Loesungen zu einem nicht genannten Pseudoproblem diskutieren.



  • Keine aktuelle std::string Implementierung wird COW verwenden, da dies spätestens seit C++11 afaik nichtmehr erlaubt ist.

    Auch STL Impls vor c++11 haben kaum Cow optimiert.
    Das interface laesst es zu, aber die Laufzeitzusagen der SGI Spec nicht.

    std::string s1 = "123456"
    std::string s2 = s1;

    s2[2] = '9'; /// bei Cow wuerde hier das kopieren stattfinden. IMHO Laut SGI Spec sollte der zugriff aber in fester Zeit ablaufen, und nicht von der Lange des Strings und dem zustand des BS abhaengen.

    Die ersten versionen der Dinkumware STL habens damit versucht und gab wohl erwartet massive probleme besonders bei Multithreading. Dann gabs Teilweisse unterschiedliche implementierungen für ST und MT .... und irgendwann (VS6.0 zeitraum) nur noch die variante ohne cow otimierung.

    Bei *nixern bin ich mir ned sicher, aber einige gcc Anwender behaupten felsenfest, das ihre STL cow optimiert .... keine Ahnung ob das stimmt.

    Wofür also überhaupt erst eine Liste machen

    Ich krieg oft interfaces vorgesetzt, wo ich Listen befüllen muss. Meist direct zu anzeigezwecken für ein Modell.
    Oder wie würdest du Liste von strings durch ein Interface schiessen, wenn das ";" da drinne implementierungsdetail ist und nicht in die GUI Schicht gehört ?
    und templates(iteratoren) in interfaces auch nicht das wahre sind ....

    std::unordered_map

    C11 darf ich leider nicht verwenden ^^ mein code muss z.b. mit vs2005 kompilierbar sein ^^ Klingt doof, ich finds auch doof, iss aber so ^^
    Und nein ich kanns ned in ne dll auslagern, dann haut mich der Distributor unserer SW ^^

    Finde ich nicht. split() lässt sich, wie oben angeführt, aus Prinzip nicht effizient implementieren und ist auch völlig unnötig,

    unnötig finde ich es nicht ....
    Ja generell wenn man performant entwickelt wird man split meiden ....
    Aber GUI eintwicklung und Bibliotheksentwicklung sind IMHO 2 paar schuhe ...
    In ner GUI lass ich viele Bereiche unoptimiert, weils keinen Intressiert, und ich eher dafür angefahren werde, wenn ich da zeit und energie verheize.
    Da will ich lieber einfachen naiv lesbaren code (wartung).
    Bei ner lib weiss ich ned wer was wie verwendet, da versuch ich am anfang schon vernuenftig performant zu entwickeln, auch wenns da bissi laenger dauert. Da krieg ich auch ned so viel Ärger, da hat mans lieber wenn die lib von haus aus gut funktioniert.

    Deswegen:
    In der Qt ist split ok,
    in der stl hat IMHO ein naives
    std::list<std::basic_string<T> > std::basic_string::split(value_type sep);
    nichts zu suchen. Weil es zu generisch und ineffizent ist.
    Man wird in der Praxis mit der STL definierte Rahmenbedingungen haben und anderes verwenden ...

    Ciao ...



  • RHBaum schrieb:

    Keine aktuelle std::string Implementierung wird COW verwenden, da dies spätestens seit C++11 afaik nichtmehr erlaubt ist.

    Auch STL Impls vor c++11 haben kaum Cow optimiert.

    Es ist legitim zu fragen, wozu es std::string braucht, wenn es schon std::vector<char> gibt. std::string mit COW ist perfekt geeignet für Datenstrukturen, die zwar eine Referenz auf einen string brauchen, aber ihn nicht kopieren möchten (die unsicherere Variante wäre const char*). Wozu man massig std::strings verändern will ist mir sowieso nicht klar.

    Ich würde sagen für single-threaded Anwendungen wäre ein std::string, bei dem COW vorgeschrieben ist besser als ein std::string, bei dem COW verboten ist.

    RHBaum schrieb:

    Das interface laesst es zu, aber die Laufzeitzusagen der SGI Spec nicht.

    std::string s1 = "123456"
    std::string s2 = s1;

    s2[2] = '9'; /// bei Cow wuerde hier das kopieren stattfinden. IMHO Laut SGI Spec sollte der zugriff aber in fester Zeit ablaufen, und nicht von der Lange des Strings und dem zustand des BS abhaengen.

    Der Zugriff ist immer noch amortisiert konstant. Entweder zahlst du bei der Kopie oder beim Zugriff, ist doch wurst wo.

    gab wohl erwartet massive probleme besonders bei Multithreading.

    massiv ist übertrieben. Ist halt langsam.

    Bei *nixern bin ich mir ned sicher, aber einige gcc Anwender behaupten felsenfest, das ihre STL cow optimiert .... keine Ahnung ob das stimmt.

    Jo, stimmt und die Standardbibliothek des GCCs macht das immer noch so um die Binärschnittstelle kompatibel zu halten. Soll sich aber demnächst ändern (clangs libc++ macht das schon richtig).

    Finde ich nicht. split() lässt sich, wie oben angeführt, aus Prinzip nicht effizient implementieren und ist auch völlig unnötig,

    unnötig finde ich es nicht ....
    Ja generell wenn man performant entwickelt wird man split meiden ....

    Wo ist das Problem?

    Ein

    for (string_ref s : split(s.begin(), s.end(), ';'))
    

    lässt sich schön lazy ohne zusätzliche Kopien lösen.

    std::list<std::basic_string<T> > std::basic_string::split(value_type sep);
    [...] Weil es zu generisch und ineffizent ist.

    lol ja, std::list ist sehr ineffizient.


  • Mod

    Nexus schrieb:

    std::string t = "text";
    std::string r = t + " und " + "mehr " + t;
    

    Wie viele temporäre Objekte hast du hier?

    Mal sehen...

    ((t + " und ") + "mehr ") + t
    // =>
    operator+(operator+(operator+(t, " und "), "mehr "), t)
    

    Und jetzt die Operatoren aus den MinGW-4.8.1 Headern

    template<typename _CharT, typename _Traits, typename _Alloc>
        inline basic_string<_CharT, _Traits, _Alloc>
        operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
    	     const _CharT* __rhs)
        {
          basic_string<_CharT, _Traits, _Alloc> __str(__lhs);
          __str.append(__rhs);
          return __str; // NRVO greift, §12.8/31
        }
    
      template<typename _CharT, typename _Traits, typename _Alloc>
        inline basic_string<_CharT, _Traits, _Alloc>
        operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
    	      const _CharT* __rhs)
        { return std::move(__lhs.append(__rhs)); }
    
      template<typename _CharT, typename _Traits, typename _Alloc>
        inline basic_string<_CharT, _Traits, _Alloc>
        operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
    	      const basic_string<_CharT, _Traits, _Alloc>& __rhs)
        { return std::move(__lhs.append(__rhs)); }
    

    Ich bin mir hier überhaupt nicht sicher, würde aber sagen, dass es zwei temporäre Objekte gibt.



  • Arcoth schrieb:

    [/code]Ich bin mir hier überhaupt nicht sicher, würde aber sagen, dass es zwei temporäre Objekte gibt.

    Ich würde behaupten, dass der Aufwand äquivalent zu 3 temporaries ist. Append wird wahrscheinlich neuen Speicher anfordern müssen und dafür den String umkopieren. Das ist am Ende so teuer wie ein temporary.

    //edit eigentlich ginge es mit nur einem temporary. Aber der Standard legt die Signatur von operator+ fest, von daher geht da nichts besseres. Sonst könnte man ein expression template basteln, welches dann einen stringbuilder emuliert.


  • Mod

    Append wird wahrscheinlich neuen Speicher anfordern müssen und dafür den String umkopieren.

    Hast Recht, soweit ich es vom groben drüberschauen richtig interpretiere

    template<typename _CharT, typename _Traits, typename _Alloc>
        basic_string<_CharT, _Traits, _Alloc>&
        basic_string<_CharT, _Traits, _Alloc>::
        append(const _CharT* __s, size_type __n)
        {
          __glibcxx_requires_string_len(__s, __n);
          if (__n)
    	{
    	  _M_check_length(size_type(0), __n, "basic_string::append");
    	  const size_type __len = __n + this->size();
    	  if (__len > this->capacity() || _M_rep()->_M_is_shared())
    	    {
    	      if (_M_disjunct(__s))
    		this->reserve(__len); // Speicher-Anforderung
    	      else
    		{
    		  const size_type __off = __s - _M_data();
    		  this->reserve(__len); // dito
    		  __s = _M_data() + __off;
    		}
    	    }
    	  _M_copy(_M_data() + this->size(), __s, __n); // Kopie
    	  _M_rep()->_M_set_length_and_sharable(__len);
    	}
          return *this;
        }
    

    Sonst könnte man ein expression template basteln, welches dann einen stringbuilder emuliert.

    So etwas ginge mit Boost.Proto, nicht wahr? Ist den das drumherum des expression template frameworks schnell genug, dass dabei etwas herausspringt?


Anmelden zum Antworten