Seite 1 von 1
std::vector-push_back mit std::move
Verfasst: 12.09.2018, 12:11
von joggel
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...^^
Re: std::vector-push_back mit std::move
Verfasst: 12.09.2018, 12:59
von dot
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…
Re: std::vector-push_back mit std::move
Verfasst: 12.09.2018, 13:43
von joggel
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...
Re: std::vector-push_back mit std::move
Verfasst: 13.09.2018, 13:55
von Helmut
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.
Re: std::vector-push_back mit std::move
Verfasst: 13.09.2018, 14:24
von dot
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
Re: std::vector-push_back mit std::move
Verfasst: 14.09.2018, 08:30
von joggel
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?
Re: std::vector-push_back mit std::move
Verfasst: 14.09.2018, 11:12
von Schrompf
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.
Re: std::vector-push_back mit std::move
Verfasst: 17.09.2018, 08:03
von joggel
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...
Re: std::vector-push_back mit std::move
Verfasst: 17.09.2018, 09:03
von Schrompf
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) );