Warum nicht std::move verhindern RVO?
In vielen Fällen, wenn wieder ein lokal von einer Funktion, RVO kicks in. Allerdings dachte ich, dass explizit mit std::move
würde zumindest durchzusetzen, sich zu bewegen, wenn RVO nicht passieren, aber, dass die RVO ist noch immer angewendet, wenn möglich. Es scheint jedoch, dass dies nicht der Fall ist.
#include "iostream"
class HeavyWeight
{
public:
HeavyWeight()
{
std::cout << "ctor" << std::endl;
}
HeavyWeight(const HeavyWeight& other)
{
std::cout << "copy" << std::endl;
}
HeavyWeight(HeavyWeight&& other)
{
std::cout << "move" << std::endl;
}
};
HeavyWeight MakeHeavy()
{
HeavyWeight heavy;
return heavy;
}
int main()
{
auto heavy = MakeHeavy();
return 0;
}
Getestet habe ich diesen code mit VC++11-und GCC-4.71", "debug" und " release-O2
) config. Die copy-ctor ist nie genannt. Der move-ctor wird nur aufgerufen, VC++11 im debug-config. Eigentlich scheint alles in Ordnung zu sein mit diesen Compilern insbesondere, aber meines Wissens RVO ist optional.
Allerdings, wenn ich explizit move
:
HeavyWeight MakeHeavy()
{
HeavyWeight heavy;
return std::move(heavy);
}
der move-ctor ist immer genannt. So versuchen, um es "sicher" macht es noch schlimmer.
Meine Fragen sind:
- Warum std::move
verhindern RVO?
- Wann ist es besser, "das beste hoffen" und sich darauf verlassen, RVO, und Wann sollte ich explizit std::move
? Oder, in anderen Worten, wie kann ich die compiler-Optimierung die Arbeit machen und noch durchzusetzen bewegen, wenn RVO nicht angewandt wird?
- Warum machen die Leute immer noch darüber reden, "das beste hoffen" in diesen Tagen? Welche compiler verwenden Sie mit C++11-Unterstützung, kann aber nicht RVO richtig?
- Copy elision (der Mechanismus hinter RVO) ist nur gestattet, unter bestimmten, strengen Bedingungen. Schreiben
std::move
verhindert, dass diese Bedingungen nicht eingehalten werden. - Und dieser Bedingungen verhindert, indem std::move sind...?
- std::move verhindert, dass
NRVO
(benanntRVO
).RVO
im Allgemeinen nicht betroffen ist (afaik) - Können Sie auf eine Referenz skizziert die Gründe, dies zu verhindern? Bewegt elision überhaupt geschehen?
- NÖ, return std::move(Schwergewicht()); dennoch ruft der move-ctor.
- Bewegen elision vorkommt, aber nicht, wenn explizit mit std::move. Lustige Sachen.
- Ich habe nachgedacht, ich war einfach nur Gründliche by doing explizit std::move, statt es scheint, ich habe sabotiert mich! 😉
- Ich habe nicht gesagt, dass der compiler
RVO
imstd::move
Fall. Ich habe nur gesagt, dass Sie nicht tun könnenNRVO
, aber (wie gesagt, afaik) noch tun könnenRVO
. Es ist die Umsetzung abhängig. - Du bist nicht allein.
- Der Umzug kann noch werden erstellte, wenn der compiler kann beweisen, dass Sie nicht das Programm ändern das Verhalten.
- Der problem Fall ist derjenige, wo der Verhaltensänderung erlaubt ist, nämlich das weglassen copy/move-Konstruktor ruft. Da der test-Fall per definition muss enthalten Nebenwirkungen, Sie beschränkt sich auf Optimierungen, die sich auf copy elision und nach den Regeln spielen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Den Fällen, in denen kopieren und verschieben elision erlaubt ist, ist in Abschnitt 12.8 §31 der Standard (version N3690):
(Die beiden Fälle, die ich ausgelassen beziehen sich auf den Fall werfen und fangen exception-Objekte, die ich für weniger wichtig halten, für die Optimierung.)
Daher in einer return-Anweisung copy-elision kann nur auftreten, wenn der Ausdruck der name einer lokalen Variablen. Wenn Sie schreiben
std::move(var)
ist, dann ist es nicht der name einer Variablen nicht mehr. Daher kann nicht der compiler zu umgehen, aber die sich bewegen, wenn es sollte dem standard entsprechen.Stephan T. Lavavej Sprach über das auf Going Native 2013 und erklärt genau Ihre situation und warum Sie zu vermeiden
std::move()
hier. Beginnt gerade bei minute 38:04. Im Grunde, wenn wieder eine lokale variable mit dem Rückgabewert-Typ, dann ist es in der Regel so behandelt, als ein rvalue deshalb ermöglichen Sie bewegen standardmäßig.return std::move
werden kann, erstellte. Wenn wir wissen, dass C++ die Referenz Rückgabewert einer Funktion ist garantiert das gleiche wie eine bestimmte Referenz Eingabewert der Funktion, ein paar interessante Ergebnisse eröffnen könnte. (Auslassung von nicht-triviale Ausdrücke, Laufzeitverlängerung der eine temporäre input-argument an eine Funktion ohne Rückgabe einer temporären, für zwei: der zweite von denen ist IMHO noch wichtiger).Wie diese:
Umwandlung der Rückkehr in eine Bewegung ist obligatorisch.
return std::move
😉move
im schlimmsten Fall. Etwas so harmloses wiereturn condition?heavy1:heavy2;
kann verhindern, dass die implizitemove
, währendif (condition) return heavy1; return heavy2;
würde nicht. Es ist etwas brüchig.