Map mit Template Typen



  • Die Memberfunktionen von unique_ptr werden komplett geinlined, da ist absolut 0 Overhead.



  • Nein, tatsächlich muss unique_ptr sogar einen zweiten Member halten (bzw. irgendwie auf ihn Verweisen): Den Deleter.

    Die Memberfunktionen von unique_ptr werden komplett geinlined, da ist absolut 0 Overhead.

    Das wird schon stimmen. Auch die vom Deleter? Sicher, auch die. Dann vielleicht tatsächlich kein Overhead.


  • Mod

    Sone schrieb:

    Nein, tatsächlich muss unique_ptr sogar einen zweiten Member halten (bzw. irgendwie auf ihn Verweisen): Den Deleter.

    Was im Falle des hier nur interessanten default_delete<T> keinen Overhead darstellt (da leere Klasse), eine vernünftige Implementierung unterstellt.



  • unique_winner schrieb:

    @DuffCola (aber auch Sone und cooky451): unique_ptr hat nicht minimalen Overhead, sondern rein gar keinen.

    Naja, unique_ptr kann schon Overhead haben.

    Beispielcode:

    namespace {
      template <typename T>
      struct unique_ptr {
        T* data;
        unique_ptr(T* data) : data(data) {}
        unique_ptr(unique_ptr&& o) : data(o.data) { o.data = 0; }
        ~unique_ptr() { delete data; }
      };
    
      unique_ptr<int> make_unique(int v)
      {
        return unique_ptr<int>(new int(v));
      }
    
      int* make_ptr(int v)
      {
        return new int(v);
      }
    }
    
    void f()
    {
      auto i = make_unique(0);
    }
    
    void g()
    {
      auto i = make_ptr(0);
      delete i;
    }
    

    gcc mit -O2 -fno-elide-constructors liefert:

    f:

    .cfi_startproc
    	pushq	%rbx
    	.cfi_def_cfa_offset 16
    	.cfi_offset 3, -16
    	movl	$4, %edi
    	call	_Znwm
    	xorl	%edi, %edi
    	movl	$0, (%rax)
    	movq	%rax, %rbx
    	call	_ZdlPv
    	xorl	%edi, %edi
    	call	_ZdlPv
    	movq	%rbx, %rdi
    	popq	%rbx
    	.cfi_def_cfa_offset 8
    	jmp	_ZdlPv
    	.cfi_endproc
    

    g:

    .cfi_startproc
    	subq	$8, %rsp
    	.cfi_def_cfa_offset 16
    	movl	$4, %edi
    	call	_Znwm
    	movl	$0, (%rax)
    	movq	%rax, %rdi
    	addq	$8, %rsp
    	.cfi_def_cfa_offset 8
    	jmp	_ZdlPv
    	.cfi_endproc
    

    Bei einem gcc mit -fno-elide-constructors sieht allerdings beides gleich aus.

    Sone schrieb:

    Nein, tatsächlich muss unique_ptr sogar einen zweiten Member halten (bzw. irgendwie auf ihn Verweisen): Den Deleter.

    Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.



  • Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.

    Ich kenne C++. Laut Standard muss unique_ptr einen Deleter halten. Wenn dieser 0 Bytes groß ist, wie bei default_deleter , wird er als Member auch wegoptimiert.



  • shared_disadvantage schrieb:

    Stichwort EBO.

    Bzw. optimal wäre tuple<T*, Deleter> .



  • Sone schrieb:

    Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.

    Ich kenne C++. Laut Standard muss unique_ptr einen Deleter halten. Wenn dieser 0 Bytes groß ist, wie bei default_deleter , wird er als Member auch wegoptimiert.

    Basiswissen C++: Member dürfen nicht wegoptimiert werden.



  • shared_disadvantage schrieb:

    Stichwort EBO.

    Bzw. optimal wäre tuple<T*, Deleter> .

    Ja, so macht es der GCC.



  • shared_disadvantage schrieb:

    Sone schrieb:

    Lern erst einmal C++ bevor du irgendwelche Behauptungen aufstellst. Stichwort EBO.

    Ich kenne C++. Laut Standard muss unique_ptr einen Deleter halten. Wenn dieser 0 Bytes groß ist, wie bei default_deleter , wird er als Member auch wegoptimiert.

    Basiswissen C++: Member dürfen nicht wegoptimiert werden.

    Eine Klasse muss mindestens einen Byte groß sein, da jedes Objekt eine Adresse haben muss.
    Und das wird bei Membern nicht wegoptimiert, wenn diese nicht "genutzt" werden? Das heißt, bei Basisklasen, die auch Subobjekte sind, darf man das - hier nicht?


  • Mod

    shared_disadvantage schrieb:

    shared_disadvantage schrieb:

    Stichwort EBO.

    Bzw. optimal wäre tuple<T*, Deleter> .

    Kaum. Genau das würde nämlich EBO unterschlagen. Tupelelemente können nicht durch unmittelbare Vererbung implementiert werden (das hätte sonst bestimmte Einschränkungen zur Folge, so könnten POD-Tuplemember nicht mehr per memcpy angefasst werden).



  • Das klingt tatsächlich doof, den der unique_ptr ist eigentlich Pflicht in modernem C++.

    Das ist doch Quatsch. Hoer doch mal mit dem Dogma auf!

    Benutze RAII wann immer es geht!

    Macht er doch, er will sie im Destruktor freigeben ...

    Wahrscheinlich meint er, weil er rohe Pointer benutzt holt er Geschwindigkeit raus.
    Aber er weis nicht, was er macht daher...

    Aus der Luft gegriffene Unterstellung.

    Den Deleter.

    Der Deleter steckt normalerweise im Typ und nicht im Objekt. 2 unique_ptr vom gleichen Typ koennen keinen unterschiedlichen deleter haben.



  • Das heißt, bei Basisklasen, die auch Subobjekte sind, darf man das - hier nicht?

    Hmm. Die Frage sollte sich wohl von selbst beantworten. Natürlich können die nicht optimiert werden...

    unique_ptr vom gleichen Typ koennen keinen unterschiedlichen deleter haben.

    Nein der Typ ihres Deleters kann nicht unterschiedlich sein, und? Was heißt das jetzt im Bezug auf den Member?

    Das ist doch Quatsch.

    Was ist Quatsch? Ich rede offensichtlich von den Fällen, wo ein roher Zeiger ein Objekt besitzt...



  • Was heißt das jetzt im Bezug auf den Member?

    Warum sollte ich den Deleter als Member halten, wenn er fuer alle Objekte gleich ist?


  • Mod

    nvm... sieht so aus als ob gccs tuple EBO auf alle leeren, nicht-finalen nicht-Tuple-Member anwendet. Hm...



  • knivil schrieb:

    Was heißt das jetzt im Bezug auf den Member?

    Warum sollte ich den Deleter als Member halten?

    Weil der Standard das sagt?

    Und trotzdem sieht man da keine Auswirkungen... hmm.. tja, GCC wendet eben EBO an. Wie camper gerade bemerkt hat.



  • camper schrieb:

    sieht so aus als ob gccs tuple EBO auf alle leeren, nicht-finalen nicht-Tuple-Member anwendet. Hm...

    Compilermagie?

    Oder leitet der tatsächlich von dem Typ ab, wenn er eine nicht-finale Klasse ist?



  • Weil der Standard das sagt?

    Na dann zitiere doch mal, wie die das begruenden.


  • Mod

    knivil schrieb:

    Weil der Standard das sagt?

    Na dann zitiere doch mal, wie die das begruenden.

    unique_ptr hat eine get_deleter Memberfunktion, die eine Referenz auf den gespeichteren Deleter zurückgibt. Der muss sich also irgendwo befinden.

    Sone schrieb:

    Oder leitet der tatsächlich von dem Typ ab, wenn er eine nicht-finale Klasse ist?

    Das. /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.1/include/g++-v4/tuple ca. Zeile 80



  • knivil schrieb:

    Weil der Standard das sagt?

    Na dann zitiere doch mal, wie die das begruenden.

    Naja, zum einen kann der Deleter State haben, der dann beim Move ebenfalls erhalten bleibt.

    Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of such a transfer, the following postconditions hold:
    [...]
    — if the pre-transfer u.d maintained state, such state has been transferred to u2.d.



  • Schaut man sich Stroustrup an, dann verbessert er das Beispiel mit rohen Zeigern wie folgt:

    Gadget* p = new Gadget{n}
    
    shared_ptr<Gadget> p{new Gadget{n}}
    
    Gadget p{n}
    

    D.h. am Ende ist es nur ein Implementierungsdetail, ob shared_ptr oder unique_ptr innerhalb von Gadget benutzt wird.


Anmelden zum Antworten