Suche gute Stringfunktion



  • Man könnte auch einen string* zurückliefern.



  • moment ...

    string new_str = func();
    

    da wird doch der operator=() aufgerufen, oder?



  • Ramsis schrieb:

    Man könnte auch einen string* zurückliefern.

    Ja, aber dann müsste ich den string in der Funktion static machen, und beim nächsten Aufruf der Funktion... was würde dann unangenehmes passieren? der alte String geht verloren?



  • Hallo

    voidpointer schrieb:

    moment ...

    string new_str = func();
    

    da wird doch der operator=() aufgerufen, oder?

    Nicht unbedingt. Siehe das hier schon oft besprochene RVO.

    bis bald
    akari


  • Mod

    voidpointer schrieb:

    2.) string func(...)
    Eigentlich eine recht gute Lösung (und ic denke die beste)... Hat nur einen entscheidenden Nachteil: Die Funktion erstellt einen String, der dann mit return zurückgegeben wird. An der Stelle des Funktionsaufrufs wird ein neuer String erstellt - also 2 Strings - ein großer Laufzeitnachteil!

    Dafür gibt es RVO. Anders sieht das bei Zuweisung aus, aber auch dafür gibt es Lösungen (und mit C++0x bekommen wir ohnehin Movekonstruktoren und eine brauchbare Lösung für das forwarding-Problem).

    3.) auto_ptr/smart_ptr auf char zurückgeben ... die haben aber auch ihre Tücken (wenn man eine Funktion mit der Kopie eines auto_ptr aufruft, hat der eigtl. auto_ptr keinen Inhalt mehr 🙄 )

    Keine echte Alternative. auto_ptr fällt aus, da dieses nicht mit Arrays umgehen kann. Prinzipiell ist es möglich ein alternatives auto_array zu erstellen (das ist sogar einfacher, weil man keine Pointerkonvertierungen berücksichtigen muss - die Tatsache der Übernahme des Ownerships ist im Übrigen die Existenzberechtigung für auto_ptr und weniger eine Tücke, natürlich ist es keine eierlegende Wollmilchsau, die plötzlich alle Probleme der Parameterübergabe löst). Ein anderer Smartpointer, der mit Arrays umgehen kann, ist möglich, aber ein rohes Array ist kein echter String. Wenn ein String das ist, was die Funktion zurückgeben soll, dann ist es falsch, umständlich etwas Anderes zurückzugeben.



  • RVO - Return Value Optimization...

    http://www.cs.cmu.edu/~gilpin/c++/performance.html#returnvalue

    Sieht also schlecht aus! Wenn ich das richtig sehe, lässt sich das nur so lösen, dass man vorher schon bei den parametern einen stringpointer übergibt, wo der string dann rein geschrieben wird?




  • Mod

    voidpointer schrieb:

    RVO - Return Value Optimization...

    http://www.cs.cmu.edu/~gilpin/c++/performance.html#returnvalue

    Sieht also schlecht aus! Wenn ich das richtig sehe, lässt sich das nur so lösen, dass man vorher schon bei den parametern einen stringpointer übergibt, wo der string dann rein geschrieben wird?

    Welches Problem vermeinst du eigentlich lösen zu müssen? Obwohl sie für gewöhnlich RVO heißt, hat die Regel, die damit gemeint ist, nicht speziell etwas mit der Rückgabe von Funktionswerten zu tun (ganz im Gegensatz zu NRVO).



  • @Camper: Ich möchte eben auch einen schönen Funktionsaufruf haben:

    string func(...);
    

    ist handlicher als

    void func(string* s, ...);
    

    Wenn ich diesen Thread richtig lese, wird also hauptsächlich daszu geraten, den konstruktor erst beim return aufzurufen?

    int func() {
    //...
    return string(...);
    }
    

    Wie hoch sind die Chancen, dass ein moderner Compiler das richtig macht? Kann ein inline die Chance erhöhen?

    Und btw: was haltet ihr von Makros? Da optimiert der Prekompiler schon alles 😉



  • voidpointer schrieb:

    1.) char* func(...)
    Als erfahrener C-Programmierer 😉 weiß ich aber, dass man dann immer wieder vergisst, den Speicher freizugebene 😕

    der erfahrene c-programmierer gibt der funktion einen string, den sie füllen kann:

    void func (char *str, size_t max)
    {
      ...
    }
    ...
    char str[...];
    func (str, sizeof(str));
    ...
    

    in c++ kannste sowas ähnliches machen:

    void func (std::string &str)
    {
      ...
    }
    ...
    std::string s;
    func (s);
    ...
    

    ohne laufzeiteinbussen durch copy-konstruktoren und heap-aktionen (ausser dass so'n std::string selber auf dem heap rumeiert).
    🙂


  • Mod

    voidpointer schrieb:

    @Camper: Ich möchte eben auch einen schönen Funktionsaufruf haben:

    string func(...);
    

    ist handlicher als

    void func(string* s, ...);
    

    genau.

    Wenn ich diesen Thread richtig lese, wird also hauptsächlich daszu geraten, den konstruktor erst beim return aufzurufen?

    Das wurde hier nirgendwo gesagt. Wie du das Funktionsergebnis innerhalb deiner Funktion berechnest, hat nichts damit zu tun, wie man unnötige Kopien bei der Übergabe von Rückgabewerten vermeidet - außer das RVO zur Vermeidung der Kopie des Rückgabewertes nur in Frage kommt, wenn das Argument von return selbst ein temporäres Objekt ist. Letzteres ist vor allem dann der Fall, wenn das Ergebnis durch einen Funktionsaufruf ensteht (in diesem Falle stellt sich nat. wieder die Frage, wie diese andere Funktion optimal zu schreiben ist) oder durch einen Konstruktoraufruf (ok, das ist auch ein Funktionsaufruf, aber wir haben hier explizit die Möglichkeit, in die Konstruktion einzugreifen).

    int func() {
    //...
    return string(...);
    }
    

    Wie hoch sind die Chancen, dass ein moderner Compiler das richtig macht? Kann ein inline die Chance erhöhen?

    Sehr hoch (99.9999%, denn diese Optimierung ist für den Compiler trivial durchzuführen), aber häufig ist diese Form gar nicht möglich, oder nur, wenn man einen bereits konstruierten String noch einmal kopiert (und diese explizite Kopie kann dann nicht eliminiert werden). Wichtig wird hier NRVO. Wenn das return-Statement ein lokales automatisches Objekt bennent, darf der Compiler die Kopie eliminieren (indem das lokale Objekt von vornherein in dem Speicher, der für das Rückgabeobjekt vorgesehen ist, konstruiert wird). Die Chancen dafür sind bei einem modernen Compiler ebenfalls sehr hoch, wenn man entsprechend strukturiert (die Regel selbst stellt eigentlich keine besonderen Bedingungen, aber eine Verletzung der folgenden Regel - die ich selbst aufgestellt habe - stellt erhöhte Anforderungen an den Compiler):
    Ein return-Statement:

    return x;
    

    kann dann optimiert werden, wenn sich innerhalb des potentiellen Scopes von x kein (erreichbares) return-Statement befindet, das nicht x benennt. Warum das so ist, kannst du dir selber überlegen:

    T foo()
    {
        if ( ... )
        {
            if ( ... )
                return T(...); // unschädlich
            T x;
            if ( ... )
                return x;
            if ( ... )
                return T(...); // schädlich, verhindert Optimierung von return x, aber dieses return kann durch RVO optimiert werden
        }
        return T( ... ); // unschädlich
    }
    

    Funktionsparamter sind ein spezieller Fall: im Prinzip gilt diese Regel auch für sie, aber ich kenne keinen Compiler, der das tatsächlich durchführt - der Grund ist klar: die Funktionsparameter werden durch den Aufrufer erzeugt.

    Und btw: was haltet ihr von Makros? Da optimiert der Prekompiler schon alles 😉

    gar nichts. Und der Präcompiler optimiert auch gar nicht.

    Eine zweite Kopieraktion steht an, wenn der Rückgabewert der Funktion im aufrufenden Ausdruck kopiert werden soll. Wenn diese Kopieraktion der Initialisierung eines Objektes dient, greift hier RVO ein. Problematisch ist dagegen Zuweisung, die kann der Compiler natürlich nicht wegoptimieren, denn der ursprüngliche Inhalt des Objektes, dem zugewiesen wird, bedarf ja möglicher bestimmter Aufräumaktionen (außer nat. die Zuweisung ist trivial, in diesem Falle hilft ggf. die as-if-Regel; aber die ist wenig hilfreich, wenn wir diskutieren, wie optimaler Code geschrieben werden muss). Wie schon erwähnt, mit Movekonstruktoren und Move-Zuweisung wird dieses Problem in C++0x gut handhabbar sein, mit der gegenwärtigen Sprache, kann man ggf. auf swap ausweichen - auch wenn es nicht besonders schön ist.



  • std::string ist eine Katastrophe für die Laufzeit wenn es darauf ankommt. Meistens ist es aber total wurscht. Viele Programmierer machen den Fehler sich über die Geschwindigkeit von Stellen in einem Programm den Kopf zu zerbrechen, wo die Geschwindigkeit vollkommen egal ist. Bei vielen Programmen gibt es nichtmal irgendwelche Stellen wo es sich auszahlen würde sich über die Geschwindigkeit Sorgen zu machen.

    Wie hoch sind die Chancen, dass ein moderner Compiler das richtig macht?

    Sehr sehr hoch (auf (N)RVO bezogen).

    Kann ein inline die Chance erhöhen?

    AFAIK nicht wirklich. Inline erlaubt den meisten Compilern andere, zusätzliche Optimierungen zu machen als (N)RVO. Solche anderen Optimierungen und (N)RVO sind auch zwei Paar Schuhe, zwei ganz unterschiedliche. (N)RVO ist im Standard festgeschrieben, und erlaubt das Weglassen von Kopien unter bestimmten Umständen, selbst wenn sich dabei das sichtbare Verhalten des Programmes ändert (!). Andere Optimierungen können nur gemacht werden wenn das sichtbare Verhalten des Programmes sich dadurch nicht ändert.

    Eine Sache die Compiler grundsätzlich daran hindert ein Stück Code wegzuoptimieren ist der Aufruf von Funktionen deren Implementierung der Compiler nicht kennt, da er davon ausgehen muss dass die Ausführung so einer Funktion "beobachtbar" wäre. Schliesslich könnte so eine Funktion ja was am Bildschirm ausgeben oder sonstwas machen. Leider fallen bei vielen Compilern (oder sogar bei allen?) auch die Funktionen "new" und "delete" in diese Kategorie, selbst wenn der User diese nicht redefiniert.
    Das führt dazu dass folgendes nicht optimiert werden kann:

    void foo()
    {
        char* ch = new char; // jeder Blinde sieht dass das hier genau garnix tut,
        delete ch; // allerdings checkt das kein mir bekannter Compiler
        // Ergebnis: die Aufrufe von "new" und "delete" werden nicht wegoptimiert
    }
    

    Und btw: was haltet ihr von Makros?

    In dem Zusammenhang sehr wenig.

    Da optimiert der Prekompiler schon alles

    Der optimiert garnix. Makros können bloss verwendet werden um Copy & Paste wie einen Funktionsaufruf aussehen zu lassen.


Anmelden zum Antworten