Kann der Compiler das optimieren?



  • xyzet schrieb:

    also ist es doch besser immer diese

    void foo(const std::list<int>& l, std::list<int>& ret) {
    ...
    }
    

    version zu verwenden

    Nein, was soll daran besser sein? Wozu sind denn Rückgabewerte da?

    Es ist furchtbar, wieviele Verbrechen im Namen der Optimierung begangen werden. Code soll in erster Linie lesbar sein! Rückgabeparameter sind nicht lesbar.

    Ich bin da sogar noch radikaler. Ich vertrete die Meinung, dass es nur eine einzige Funktion gibt, in der eine By-Ref-Rückgabe erlaubt sein sollte, nämlich die 'swap'-Funktion. Alles andere sollte gefälligst per Rückgabewert erledigt werden. und 'swap' wird auch nur deswegen von mir geduldet, weil C++ keine effiziente Semantik hat, um die doppelten Rückgabewerte zu verarbeiten.


  • Mod

    xyzet schrieb:

    weil wir nicht wissen, wie ein anderer unsere funktion aufruft

    Konzentriere dich beim Schreiben einer Funktion auf die Funktion der Funktion. Was der Aufrufer damit anstellt, ist seine Sache und geht dich als Funktionsschreiber nichts an - Die Signatur der Funktion zu ändern entspricht in etwa einem Pop-up, dass jedesmal auftaucht, wenn ein Aufruf geschrieben werden soll: 'Heh, Aufrufer, ich weiß, dass du ein Idiot bist und habe mir meinen Kopf für dich zerbrochen und alles gaaaaaaaaaaaaaanz einfach gemacht'. Eine normale Funktionsrückgabe favorisiert keine bestimmt Optimierung und verhindert auch keine. Das ist nicht so, wenn konstante Objekte zurückgegeben werden oder Referenzargumente benutzt werden.



  • Kann hier Konrad und Camper voll zustimmen. Rückgabe-Parameter sind genauso schlecht, wie Leute die aus angeblichen Performance-Gründen in C++ C-Code schreiben und z.B. die Std-Lib meiden. Das ist nicht der Sinn Sprachmittel (wie den Returnwert) einzuführen, und sie dann zu meiden.



  • Habe trotzdem noch ne Frage:

    class A
    {
        string s;
    public:
        string get_s()
        {
             return s;
        }
    };
    

    Hier wird ja keine temporäre Variable zurück gegeben. Gelten die gleichen Regeln wie oben?


  • Mod

    s ist weder ein temporäres Objekt noch eine automatische Variable - der Aufruf des Copy-Konstruktors ist daher zwingend.

    cout << (foo.get_s()+="bar");
    


  • woher weiß der compiler, dass er diese nrvo machen darf, wenn er die aufgerufene funktion nicht kennt? oder wird einfach dann sozusagen per default die referenz auf die "rückgabe"-instanz auf den stack geschrieben und die funktion kann sich dann aussuchen, ob sie diese referenz verwendet oder nicht? das wäre dann eine sache der abi, sodass diese optimierung voraussetzt, dass der compiler, der den funktionsaufruf erzeugt, auch dieses abi unterstützt.



  • Artchi schrieb:

    Rückgabe-Parameter sind genauso schlecht, wie Leute die aus angeblichen Performance-Gründen in C++ C-Code schreiben und z.B. die Std-Lib meiden. Das ist nicht der Sinn Sprachmittel (wie den Returnwert) einzuführen, und sie dann zu meiden.

    Hier fühle ich mich nun doch gezwungen zu widersprechen. Was den Rückgabewert angeht, stimme ich für den allgemeinen Fall zu, jedoch halte ich den Vergleich mit der C++-Standard-Library, die in viel zu vielen relevanten Bereichen ihre Schwächen hat, für sehr unangebracht.
    C-Dateistreams sind schneller, sprintf ist schneller und kleiner, STL-Container werden schnell zur Bloatware. Meßbare Fakten.

    Zum Thema Rückgabewerte: allgemein sollte man sie freilich verwenden, aber wenn eine Funktion mehr als einen Wert zurückgibt (das Windows-API ist ja voll solcher Funktionen), ist die Lösung über Referenzparameter die einzig sinnvolle und übersichtliche. Dokumentation ist alles.

    Wenn das Kopieren eines Rückgabewertes Potential zum Bottleneck hat, gebe ich einfach stattdessen einen referenzzählenden Smartpointer darauf zurück.

    @namenlos: Rückgabewerte größeren Typs werden i.d.R. ohnehin zuerst, wenn nötig, vom Aufrufer auf dem Stack allokiert und dann per Referenzparameter angesprochen, so daß die aufgerufene Funktion eine lokale Instanz des Typs auch gleich an diese Stelle schreiben kann.



  • audacia schrieb:

    Artchi schrieb:

    Rückgabe-Parameter sind genauso schlecht, wie Leute die aus angeblichen Performance-Gründen in C++ C-Code schreiben und z.B. die Std-Lib meiden. Das ist nicht der Sinn Sprachmittel (wie den Returnwert) einzuführen, und sie dann zu meiden.

    Hier fühle ich mich nun doch gezwungen zu widersprechen. Was den Rückgabewert angeht, stimme ich für den allgemeinen Fall zu, jedoch halte ich den Vergleich mit der C++-Standard-Library, die in viel zu vielen relevanten Bereichen ihre Schwächen hat, für sehr unangebracht.
    C-Dateistreams sind schneller, sprintf ist schneller und kleiner, STL-Container werden schnell zur Bloatware. Meßbare Fakten.

    Erwiesener Schwachsinn. Mag für einige Implementierungen zustimmen, aber nicht für alle.

    'sprintf' *kann* theoretisch gar nicht so schnell implementiert werden wie ein gut genutzter Stringstream, weil 'sprintf' zur Laufzeit den Formatstring parsen muss. STL-Container als Bloatware kann ich nicht nachvollziehen. Und dass C-Datenstreams überall schneller sind, stimmt so auch nicht. Ich habe schon gegenteilige Messergebnisse gelesen.

    Zum Thema Rückgabewerte: allgemein sollte man sie freilich verwenden, aber wenn eine Funktion mehr als einen Wert zurückgibt (das Windows-API ist ja voll solcher Funktionen), ist die Lösung über Referenzparameter die einzig sinnvolle und übersichtliche.

    Nein, Quatsch. Wozu gibt es Tupel?



  • Konrad Rudolph schrieb:

    Erwiesener Schwachsinn. Mag für einige Implementierungen zustimmen, aber nicht für alle.

    Für die meisten trifft es leider zu.

    Konrad Rudolph schrieb:

    'sprintf' *kann* theoretisch gar nicht so schnell implementiert werden wie ein gut genutzter Stringstream, weil 'sprintf' zur Laufzeit den Formatstring parsen muss.

    Theoretisch, ja. In der Praxis ist sprintf meist nicht nur schneller und kleiner, sondern auch viel einfacher für die Lokalisierung. Und dank der Spracherweiterungen von C++ kann man auch typsichere sprintf-Versionen schreiben.

    Nein, Quatsch. Wozu gibt es Tupel?

    Schauen wir mal:

    BOOL ReadFile(
    
        HANDLE hFile,	// handle of file to read 
        LPVOID lpBuffer,	// address of buffer that receives data  
        DWORD nNumberOfBytesToRead,	// number of bytes to read 
        LPDWORD lpNumberOfBytesRead,	// address of number of bytes read 
        LPOVERLAPPED lpOverlapped 	// address of structure for data 
       );
    

    würde zu

    struct READFILE_RESULT
    {
        BOOL bSuccess;
        std::vector <BYTE> vBuffer;
        OVERLAPPED overlappedOut;
    }
    
    READFILE_RESULT MyReadFile (HANDLE hFile, DWORD nNumberOfBytesToRead, OVERLAPPED overlappedIn);
    

    Zweifelsohne wunderschön. Der Compiler wird es schon wegoptimieren können.

    Wäre ReadFile freilich in anständigem C++ geschrieben, so hätte man das Problem mit OVERLAPPED freilich einfacher lösen können, indem man es als private Membervariable einer File-Klasse implementiert hätte, so daß es in der Schnittstelle gar nicht auftaucht. Dafür müßte man eben noch die Funktion überladen:

    READFILE_RESULT File::ReadFile (DWORD nNumberOfBytesToRead);
    READFILE_RESULT File::ReadFile (DWORD nNumberOfBytesToRead, DWORD dwOffs);
    READFILE_RESULT File::ReadFile (DWORD nNumberOfBytesToRead, HANDLE hOverlappedEvent);
    READFILE_RESULT File::ReadFile (DWORD nNumberOfBytesToRead, DWORD dwOffs, HANDLE hOverlappedEvent);
    

    Außerdem paßt uns dann natürlich die Fixierung auf std::vector nicht. Vielleicht will ja jemand einen anderen Container verwenden? Wir implementieren ReadFile also für beliebige Container folgendermaßen:

    template <typename T, template <typename> class Cont>
      struct READFILE_RESULT
    {
        BOOL bSuccess;
        Cont <T> buffer;
    }
    template <typename T, template <typename> class Cont>
      READFILE_RESULT <T, Cont <T> > File::ReadFile (DWORD nNumberOfItemsToRead /* ... */)
    { // template-Funktionen werden natürlich im Header implementiert. Äußerst praktisch besonders für Windows-eigene Funktionen.
    }
    

    SCNR. Natürlich gibt es viele Fälle, in denen auch Tupel sinnvoll wären, aber bei manchen, insbesondere bei Funktionen mit Parametern, die sowohl gelesen als auch geschrieben werden, ist die ausschließliche Beschränkung auf den Rückgabewert hinderlich.



  • audacia schrieb:

    Konrad Rudolph schrieb:

    'sprintf' *kann* theoretisch gar nicht so schnell implementiert werden wie ein gut genutzter Stringstream, weil 'sprintf' zur Laufzeit den Formatstring parsen muss.

    Theoretisch, ja. In der Praxis ist sprintf meist nicht nur schneller und kleiner, sondern auch viel einfacher für die Lokalisierung. Und dank der Spracherweiterungen von C++ kann man auch typsichere sprintf-Versionen schreiben.

    du meinst variadic templates? die sind aber noch nicht so verbreitet afaik.

    struct READFILE_RESULT
    {
        BOOL bSuccess;
        std::vector <BYTE> vBuffer;
        OVERLAPPED overlappedOut;
    }
    
    READFILE_RESULT MyReadFile (HANDLE hFile, DWORD nNumberOfBytesToRead, OVERLAPPED overlappedIn);
    

    ich glaube fast, er bezog sich auf tr1::tuple und nicht auf die WinAPI.



  • Wenns zwei Werte sind, kann man auch schon pair benutzen:

    std::pair<bool, std::vector<int>> foo();
    

    Wenns mehr sein soll, dann tuple aus dem TR1, wie oben gesagt. Ist das gleiche Prinzip wie pair, nur halt mit mehr möglichen Werten.

    Wie immer: die wenigsten kennen die Standardlib und ihre Möglichkeiten und haben deshalb Vorurteile.



  • queer_boy schrieb:

    du meinst variadic templates? die sind aber noch nicht so verbreitet afaik.

    Ein wenig eingeschränkt und umständlicher geht es ja auch ohne (indem man die Funktion ~20x überlädt). Nicht schön, aber möglich, und es hat, wie du sagst, Potential, bald viel einfacher lösbar zu werden.

    queer_boy schrieb:

    ich glaube fast, er bezog sich auf tr1::tuple und nicht auf die WinAPI.

    Dann denk dir READFILE_RESULT doch einfach als ausgeschriebenes Tuple 😉



  • wie du selbst schon angesprochen hast, könnte man die WinAPI in C++ auch ganz anders schreiben. nur war ich mir nicht mehr sicher, wie sehr ich darauf eingehen sollte, weil man nicht genau erkennen kann, an welcher stelle der sarkasmus beginnt 😉

    btw. häng an die ~20 vielleicht noch eine null dran, und für jeden eigenen typ auch noch mal soviele (für alle kombinationen) - variadic templates oder gar nicht erst in die versuchung kommen, sondern gleich die typsicheren streams verwenden.



  • queer_boy schrieb:

    btw. häng an die ~20 vielleicht noch eine null dran, und für jeden eigenen typ auch noch mal soviele (für alle kombinationen)

    Ungeachtet der Ernsthaftigkeit des Vorschlages: weshalb für jeden eigenen Typ nochmal so viele? Für 0-19 Parameter reichen doch 20 Überladungen, oder?

    queer_boy schrieb:

    variadic templates oder gar nicht erst in die versuchung kommen, sondern gleich die typsicheren streams verwenden.

    Was macht denn boost::function?



  • audacia schrieb:

    queer_boy schrieb:

    variadic templates oder gar nicht erst in die versuchung kommen, sondern gleich die typsicheren streams verwenden.

    Was macht denn boost::function?

    Was hat das mit 'boost::function' zu tun? Die sind doch typensicher.

    Btw, zur Tupel-Rückgabe: natürlich ist die WinAPI eingeschränkt, da sie eine sehr typenbeschränkte Schnittstelle darstellt, um möglichst vielseitig einsetzbar zu sein. Dass man auch heute noch größtenteils an einer C-Schnittstelle für jegliches Interop hängt, ist natürlich extrem schade, weil es doch die Möglichkeiten extrem beengt.

    Die WinAPI ist ja geradezu das Paradebeispiel, wie man es nicht machen sollte. Sowas passiert eben, wenn man versucht, in C OOP zu programmieren.



  • Konrad Rudolph schrieb:

    Was hat das mit 'boost::function' zu tun? Die sind doch typensicher.

    boost::function ist nicht mittels Variadic Templates, sondern über die Mehrfachüberladung implementiert, vor der queer_boy mich so dringend warnte.

    Konrad Rudolph schrieb:

    Btw, zur Tupel-Rückgabe: natürlich ist die WinAPI eingeschränkt, da sie eine sehr typenbeschränkte Schnittstelle darstellt, um möglichst vielseitig einsetzbar zu sein.

    Vielleicht hätte ich den Sarkasmus noch deutlicher durchblicken lassen sollen.
    Hältst du meine ReadFile-Versionen im Ernst für in irgendeiner Weise praxistauglich?

    Konrad Rudolph schrieb:

    Dass man auch heute noch größtenteils an einer C-Schnittstelle für jegliches Interop hängt, ist natürlich extrem schade, weil es doch die Möglichkeiten extrem beengt.

    Die WinAPI ist ja geradezu das Paradebeispiel, wie man es nicht machen sollte. Sowas passiert eben, wenn man versucht, in C OOP zu programmieren.

    Das sehe ich anders. Ein Betriebssystem wie Windows _sollte_ IMHO eine C-Schnittstelle haben und nicht eine, die es auf eine der Hochsprachen und ein ABI beschränkt - es sei denn, es ist für mehrere Sprachen entworfen wie das .NET-Framework, und selbst das ist ja in C++ nur mit einer gewaltigen Verrenkung namens C++/CLI verwendbar.



  • audacia schrieb:

    Konrad Rudolph schrieb:

    Was hat das mit 'boost::function' zu tun? Die sind doch typensicher.

    boost::function ist nicht mittels Variadic Templates, sondern über die Mehrfachüberladung implementiert, vor der queer_boy mich so dringend warnte.

    Ja, weil es variadic templates eben noch nicht gibt.

    Konrad Rudolph schrieb:

    Btw, zur Tupel-Rückgabe: natürlich ist die WinAPI eingeschränkt, da sie eine sehr typenbeschränkte Schnittstelle darstellt, um möglichst vielseitig einsetzbar zu sein.

    Vielleicht hätte ich den Sarkasmus noch deutlicher durchblicken lassen sollen.
    Hältst du meine ReadFile-Versionen im Ernst für in irgendeiner Weise praxistauglich?

    Nein, die Version ist natürlich schlecht und man würde die ganz anders implementieren. Aber ich halte trotzdem keine Version mit Ausgabe-Parametern für sinnvoll. Übrigens benutzt die STL Mechanismen, die recht ähnlich sind, z.B. die 'insert'-Methode von Containern.

    Konrad Rudolph schrieb:

    Dass man auch heute noch größtenteils an einer C-Schnittstelle für jegliches Interop hängt, ist natürlich extrem schade, weil es doch die Möglichkeiten extrem beengt.

    Die WinAPI ist ja geradezu das Paradebeispiel, wie man es nicht machen sollte. Sowas passiert eben, wenn man versucht, in C OOP zu programmieren.

    Das sehe ich anders. Ein Betriebssystem wie Windows _sollte_ IMHO eine C-Schnittstelle haben und nicht eine, die es auf eine der Hochsprachen und ein ABI beschränkt - es sei denn, es ist für mehrere Sprachen entworfen wie das .NET-Framework, und selbst das ist ja in C++ nur mit einer gewaltigen Verrenkung namens C++/CLI verwendbar.

    Ich habe ja auch nicht gesagt, dass man es besser machen könnte. Die WinAPI ist gewissermaßen das kleinste Übel. Trotzdem ist sie schlimm.



  • Konrad Rudolph schrieb:

    Ja, weil es variadic templates eben noch nicht gibt.

    Genau darum sehe ich im Gegensatz zu queer_boy auch keine Verwerflichkeit darin, sich, solange das der Fall ist, dieses Hilfsmittels zu bedienen.

    Konrad Rudolph schrieb:

    Aber ich halte trotzdem keine Version mit Ausgabe-Parametern für sinnvoll. Übrigens benutzt die STL Mechanismen, die recht ähnlich sind, z.B. die 'insert'-Methode von Containern.

    Du bist dir der Einschränkung, der man sich unterzieht, wenn man nicht direkt in einen Puffer lesen lassen kann, weil man den als Parameter übergeben müßte, bewußt?

    Konrad Rudolph schrieb:

    Ich habe ja auch nicht gesagt, dass man es besser machen könnte. Die WinAPI ist gewissermaßen das kleinste Übel. Trotzdem ist sie schlimm.

    Achso, du meinst so ähnlich wie bei Churchill und der Demokratie? Dann kann ich dir natürlich beipflichten. 😉



  • Konrad Rudolph schrieb:

    Learning Java: Better never than late.

    Dabei kannst du in Java einfach beliebig Pointer auf Klassen zurückgeben und der GC kümmert sich um den Rest. Past doch besser zu deinen Wünschen als C++.



  • audacia schrieb:

    Konrad Rudolph schrieb:

    Aber ich halte trotzdem keine Version mit Ausgabe-Parametern für sinnvoll. Übrigens benutzt die STL Mechanismen, die recht ähnlich sind, z.B. die 'insert'-Methode von Containern.

    Du bist dir der Einschränkung, der man sich unterzieht, wenn man nicht direkt in einen Puffer lesen lassen kann, weil man den als Parameter übergeben müßte, bewußt?

    Hmm, nein, ehrlich gesagt gerade nicht. Kann sein, dass ich auf dem Schlauch stehe aber was hindert einen daran, einen Proxy zurückzugeben, der operator= überlädt und erst zum Zeitpunkt der Zuweisung das Auslesen übernimmt?

    Konrad Rudolph schrieb:

    Ich habe ja auch nicht gesagt, dass man es besser machen könnte. Die WinAPI ist gewissermaßen das kleinste Übel. Trotzdem ist sie schlimm.

    Achso, du meinst so ähnlich wie bei Churchill und der Demokratie?

    Jupp, genauso. Das Leben [eines Programmierers] besteht aus faulen Kompromissen. 😉

    haha. schrieb:

    Konrad Rudolph schrieb:

    Learning Java: Better never than late.

    Dabei kannst du in Java einfach beliebig Pointer auf Klassen zurückgeben und der GC kümmert sich um den Rest. Past doch besser zu deinen Wünschen als C++.

    Ich halte mich da ganz nach Stepanov. C++ mag fehlerbehaftet sein (ich *hasse* diese Syntax), ist aber trotzdem brillant. Java ist einfach uninteressant. „… it has no intellectual value whatsoever.“ Natürlich muss man immer vorsichtig sein, wenn man einen Polemiker wie Stepanov zitiert, aber konziser kann man es einfach nicht sagen. (Für die anderen: ich halte jetzt meinen Mund. Das wird kein Sprachen-Krieg-Thread.)


Anmelden zum Antworten