std::vector-push_back mit std::move

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
joggel
Establishment
Beiträge: 1413
Registriert: 06.11.2007, 19:06

std::vector-push_back mit std::move

Beitrag von joggel » 12.09.2018, 12:11

Hallo,

nur mal eine eher kurze und recht simple Frage. Es geht um das verwenden von std::move.
Ich habe mal den relevanten Code auf ein minimum reduziert.

ich habe folgenden Code:

Code: Alles auswählen

for(const auto& object : mMyVector)
{
	if(someExpresssion)
		mCollectorClass.addObject(object);
}
wobei

Code: Alles auswählen

CollectorClass::addObject(const std::shared_ptr<Object>& anObject)
{
	myObjectVector.push_back(anObject);
}
nun Frage ich mich, ob ich nicht eher sowas schreiben kann:

Code: Alles auswählen

CollectorClass::addObject(const std::shared_ptr<Object>&& anObject)
{
	myObjectVector.push_back(anObject);
}
wäre das nicht besser?
Ich habe das nämlich probiert, der vector mMyVector ist danach aber unverändert.
Mir scheint es auch so, als ob das push_back von std::vector keine überladung vom &&-Parameter hat.
Geht das also gar nicht was ich vorhabe?
Und wie müsste ich das denn realizieren?


Wie gesagt, sehr simple frage; modern C++ und so...^^
CEO of Dirty Codez Production®

Benutzeravatar
dot
Establishment
Beiträge: 1657
Registriert: 06.03.2004, 19:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: std::vector-push_back mit std::move

Beitrag von dot » 12.09.2018, 12:59

joggel hat geschrieben:nun Frage ich mich, ob ich nicht eher sowas schreiben kann:

Code: Alles auswählen

CollectorClass::addObject(const std::shared_ptr<Object>&& anObject)
{
	myObjectVector.push_back(anObject);
}
wäre das nicht besser?
Wenn, dann

Code: Alles auswählen

CollectorClass::addObject(std::shared_ptr<Object>&& anObject)
{
    … 
}
Eine rvalue Reference to non-const ist in der Regel sinnlos, weil man davon nicht moven kann (move Constructor erfordert normalerweise non-const source).
joggel hat geschrieben:Ich habe das nämlich probiert, der vector mMyVector ist danach aber unverändert.
Was genau meinst du mit "unverändert"? Wenn wirklich ein push_back() gemacht wird, dann muss der vector ein weiteres Element bekommen oder eine Exception fliegen. Ansonsten ist das ein extrem gravierender Bug in der Standardlibraryimplementierung deines Compilers. Dass push_back() erfolgreich returned aber einfach nichts tut, gibt es nicht…
joggel hat geschrieben:Mir scheint es auch so, als ob das push_back von std::vector keine überladung vom &&-Parameter hat.
Doch, anObject ist aber kein rvalue (eine named rvalue Reference bindet nur an rvalues, ist selbst aber ein lvalue), du müsstest explizit moven.

Abgesehen davon: emplace_back() is bae ;)

Und rein prinzipiell: Wieso shared_ptr? Hast du tatsächlich eine shared ownership Situation? shared_ptr sollte eigentlich nur in extrem seltenen Ausnahmefällen erforderlich sein. Für den Normalfall nimmt man unique_ptr

joggel
Establishment
Beiträge: 1413
Registriert: 06.11.2007, 19:06

Re: std::vector-push_back mit std::move

Beitrag von joggel » 12.09.2018, 13:43

dot hat geschrieben: ...
joggel hat geschrieben:Ich habe das nämlich probiert, der vector mMyVector ist danach aber unverändert.
Was genau meinst du mit "unverändert"? Wenn wirklich ein push_back() gemacht wird, dann muss der vector ein weiteres Element bekommen oder eine Exception fliegen. Ansonsten ist das ein extrem gravierender Bug in der Standardlibraryimplementierung deines Compilers. Dass push_back() erfolgreich returned aber einfach nichts tut, gibt es nicht…
mMyVector ist der Vector von dem ich die einträge moven möchte. Also über den ich iteriere und dann ggf. Einträge heraus nehme (eben mit move).
Aber ich werd es noch mal probieren mit non-const reference.
dot hat geschrieben: Und rein prinzipiell: Wieso shared_ptr? Hast du tatsächlich eine shared ownership Situation? shared_ptr sollte eigentlich nur in extrem seltenen Ausnahmefällen erforderlich sein. Für den Normalfall nimmt man unique_ptr
Ach... ich sitze hier an einem projekt, wo die eigentlich _fast_ alles in über ein std::shared_ptr gemacht haben. Ich würde das auch eher in 95% der fälle über einen raw-Zeiger machen.

Und emplace_back() schaue ich mir mal an.
Danke, ich werde das mal über diesen Weg probieren...

Edit:
Ja, ich würde auch eher fast alles mit unique_ptr machen und nur raw-pointer rausreichen, aber hier reichen die fast alles über shard_ptr raus, was ich auch fraglich finde...
CEO of Dirty Codez Production®

Helmut
Establishment
Beiträge: 230
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: std::vector-push_back mit std::move

Beitrag von Helmut » 13.09.2018, 13:55

Probier's mal so:

Code: Alles auswählen

for(auto& object : mMyVector)
{
	if(someExpresssion)
		mCollectorClass.addObject(std::move(object));
}
mit

Code: Alles auswählen

CollectorClass::addObject(std::shared_ptr<Object>&& anObject)
{
	myObjectVector.push_back(std::move(anObject));
}
Dabei gehe ich davon aus, dass mMyVector ein vector<std::shared_ptr<Object>> ist.
Ich empfehle übrigens auch, std::shared_ptr möglichst zu vermeiden. Aber wenn du es nutzt, lohnt es sich nicht wirklich sich über Move-Semantik Gedanken zu machen. Einen std::shared_ptr zu kopieren ist nicht sonderlich teuer.

Benutzeravatar
dot
Establishment
Beiträge: 1657
Registriert: 06.03.2004, 19:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: std::vector-push_back mit std::move

Beitrag von dot » 13.09.2018, 14:24

Helmut hat geschrieben:Aber wenn du es nutzt, lohnt es sich nicht wirklich sich über Move-Semantik Gedanken zu machen. Einen std::shared_ptr zu kopieren ist nicht sonderlich teuer.
Hängt davon ab. Auch wenn es hier vermutlich kein großes Problem ist: Einen shared_ptr zu kopieren ist vermutlich doch um einiges teurer als einen unique_ptr zu kopieren. Nicht nur weil shared_ptr doppelt so groß ist, sondern insbesondere auch weil das Referencecountupdate ein atomic Fetch-and-add erfordert, welches vermutlich kaum ein Compiler wegoptimieren wird…

Beispiel clang:

Code: Alles auswählen

void add(std::vector<std::shared_ptr<int>>& bla, std::shared_ptr<int>&& p)
{
    bla.emplace_back(p);
}

Code: Alles auswählen

add(…)
  mov rax, qword ptr [rdi + 8]
  cmp rax, qword ptr [rdi + 16]
  jae .LBB0_4
  mov rcx, qword ptr [rsi]
  mov qword ptr [rax], rcx
  mov rcx, qword ptr [rsi + 8]
  mov qword ptr [rax + 8], rcx
  test rcx, rcx
  je .LBB0_3
  lock add qword ptr [rcx + 8], 1    ; ref count update (!?)
  mov rax, qword ptr [rdi + 8]
.LBB0_3:
  add rax, 16
  mov qword ptr [rdi + 8], rax
  ret
.LBB0_4:
  jmp void std::__1::vector<std::__1::shared_ptr<int>, std::__1::allocator<std::__1::shared_ptr<int> > >::__emplace_back_slow_path<std::__1::shared_ptr<int>&>(std::__1::shared_ptr<int>&) # TAILCALL
vs

Code: Alles auswählen

void add(std::vector<std::shared_ptr<int>>& bla, std::shared_ptr<int>&& p)
{
    bla.emplace_back(std::move(p));
}

Code: Alles auswählen

add(…)
  mov rax, qword ptr [rdi + 8]
  cmp rax, qword ptr [rdi + 16]
  jae .LBB0_2
  movups xmm0, xmmword ptr [rsi]
  movups xmmword ptr [rax], xmm0
  xorps xmm0, xmm0
  movups xmmword ptr [rsi], xmm0
  add qword ptr [rdi + 8], 16
  ret
.LBB0_2:
  jmp void std::__1::vector<std::__1::shared_ptr<int>, std::__1::allocator<std::__1::shared_ptr<int> > >::__emplace_back_slow_path<std::__1::shared_ptr<int> >(std::__1::shared_ptr<int>&&) # TAILCALL

joggel
Establishment
Beiträge: 1413
Registriert: 06.11.2007, 19:06

Re: std::vector-push_back mit std::move

Beitrag von joggel » 14.09.2018, 08:30

Also wieder was gelernt (was ja auch total logisch ist): move bedarf non-const reference^^

Und ja, shared_ptr zu moven ist auch nicht gerade der sinnvollste Anwendungsfall. Aber mir geht es eher darum, dass ich versuche die "neuen" Features überall zu nutzen.

Danke für die Erklärung.

Edit:
Aber nur mal eine hypothetische Frage:
Was spricht eigentlich dagegen so oft wie möglich shared_ptr zu verwenden?
CEO of Dirty Codez Production®

Benutzeravatar
Schrompf
Moderator
Beiträge: 3848
Registriert: 26.02.2009, 00:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: std::vector-push_back mit std::move

Beitrag von Schrompf » 14.09.2018, 11:12

shared_ptr allokiert zwangsweise bei jedem Erzeugen, wenn Du nicht std::make_shared verwendest, sogar zweimal. Viele Sachen kann man stattdessen einfach einfach auf dem Stack anlegen. Und ich persönlich finde, dass shared_ptr nur ne Ausrede ist, um sich keine Gedanken über Lebenszeit und Besitz machen zu müssen.
Häuptling von Dreamworlds. Baut an was Neuem. Hilft nebenbei nur höchst selten an der Open Asset Import Library mit.

joggel
Establishment
Beiträge: 1413
Registriert: 06.11.2007, 19:06

Re: std::vector-push_back mit std::move

Beitrag von joggel » 17.09.2018, 08:03

Okay, das mit Allokation ist wirklich ein Argument; zumindest wenn man viele shared_ptrs hat.
Und das das auch irgendwie ein falsches SW-Designverständis ist sehe ich auch so...
CEO of Dirty Codez Production®

Benutzeravatar
Schrompf
Moderator
Beiträge: 3848
Registriert: 26.02.2009, 00:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: std::vector-push_back mit std::move

Beitrag von Schrompf » 17.09.2018, 09:03

Manchmal geht's nicht anders - wenn man mit externen Libs arbeitet, oder bei so asynchronen Basteleien mit Callbacks aus anderen Threads und wasweißichnoch. Aber hier isses echt simpel. Du nimmst einen std::vector aus echten Value Objekts, also schlicht std::vector<Object>. Und Du konstruierst neue Objekte direkt darin, also mit objects.emplace_back( ctor, params, ...);. Und wenn Du bereits ein Objekt hast, dann bewegst Du es in die Sammlung hinein mit objects.push_back( std::move(my_object) );
Häuptling von Dreamworlds. Baut an was Neuem. Hilft nebenbei nur höchst selten an der Open Asset Import Library mit.

Antworten