C++11 Frage
-
Ja gut, man müsste es eben noch löschen.
MfG
-
Auch dann würd' kopiert!
-
Cyres schrieb:
Die Begriffe sind eigentlich selbsterklärend: && ist eine Referenz auf ein rvalue, also in etwa
Foo&& i = Foo(10); Foo j = i;Der Default-Konstruktor von Foo wird in der ersten Zeile aufgerufen, der Move-Konstruktor in der zweiten Zeile.
Ich hab es zwar nicht getestet, aber da i einen Namen hat, wird denke ich der normale Copy-Contruktor aufgerufen...
Zudem meine ich mich zu erinnern, dass man einen Rückgabewert (hier halt der Konstruktor) nicht als rvalue-Referenz speichert.
Du möchtest wohl eher etwas wie:
Foo i(10); Foo j = std::nove(i);Gruß,
XSpilleWenn ich völlig falsche liege, korrigiert mich bitte...
-
Benannte RValue Referenzen sind LValues.
-
Hier der 'Beweis' (ohne Optimierungen kompilieren!):
#include <string> #include <iostream> struct Foo{ Foo(int v) : i(new int(v)){ } Foo(Foo&& foo) : i(foo.i) { foo.i = 0; std::cout << "move" << std::endl; } Foo(const Foo& foo) : i(new int(*foo.i)) { std::cout << "copy" << std::endl; } ~Foo(){ delete i; } int* i; }; int main(){ Foo&& i = Foo(10); // <-- Hier Foo j = i; Foo i2 = Foo(10); Foo j2 = std::move(i2); }copy
moveWie Kellerautomat gemeint hat: sobald er einen Namen hat, ist es wieder ein lvalue...
Aber vielleicht bestätigt mir noch jemand, dass man die markierte Zeile ebenfalls nicht so verwenden sollte...
-
Die Zeile ist zwar nicht besonders sinnvoll, aber auch nicht falsch.
-
Also ich hätte z.B. erwartet, dass dieser Code crashed:
void fill_stack(int i){ if(i<1000){ std::cout << i <<std::endl; fill_stack(++i); } } Foo f(){ return Foo(10); } int main(){ Foo&& i = f(); std::cout << *i.i << std::endl; fill_stack(0); Foo j = i; std::cout << *j.i << std::endl; }Also Foo(10) liegt auf dem Stack und danach wird der Stack durch den Funktionsaufruf überschrieben...
Und eine rvalue-Referenz, ist ja auch nur eine Referenz...Ich vermute es liegt nur an 'Optimierungen' des Compilers?
-
So funktionieren RValue Referenzen aber nicht. Die Lebenszeit des temporaeren Objektes wird bis zum Scopeende verlaengert, genau wie wenn du eine LValue Referenz auf const verwendet haettest.
-
Kellerautomat schrieb:
So funktionieren RValue Referenzen aber nicht. Die Lebenszeit des temporaeren Objektes wird bis zum Scopeende verlaengert, genau wie wenn du eine LValue Referenz auf const verwendet haettest.
Stimmt...
Hab ich auch schonmal gehört!!! THX 
-
@yihaaa Mit Basistypen machen Rvalue-Referenzen ja auch wenig Sinn. (Zumindest fällt mir da keine Anwendung ein.) Aber die übergibst du ja auch nicht per Referenz (außer du willst sie verändern) aber der Punkt ist, dass kopieren nichts kostet. Interessant sind RValue Referenzen z.B. bei Containern wie std::vector. Angenommen du hast eine Klasse, die im Konstruktor einen std::vector will und den dann abspeicherst. In C++03 hätte man jetzt eine const& genommen, und den vector dann kopiert. Wenn der vector sonst aber garnicht mehr gebraucht wurde, war das eine unnötige Kopie. Jetzt kann man den vector aber "moven", dadurch wird die Aktion dann etwa so teuer wie das Kopieren eines Pointers.
-
Eher so teuer wie das Kopieren einer struct mit drei Zeigern

-
@cooky451: Ja das mit den Basistypen habe ich jetz verstanden. Trotzdem vielen Dank für die Erklärung.
Ist es richtig, dass std::move einfach nur ein typecast ausführt? Sowas in der Art?
reinterpret_cast< Type&& >( x );MfG
-
yihaaa schrieb:
@cooky451: Ja das mit den Basistypen habe ich jetz verstanden. Trotzdem vielen Dank für die Erklärung.
Ist es richtig, dass std::move einfach nur ein typecast ausführt? Sowas in der Art?
reinterpret_cast< Type&& >( x );MfG
Es ist ein cast, allerdings kein reinterpret_cast, sondern etwas normales (static_cast). Wieso wird eigentlich bei Fragen immer gleich reinterpret_cast ausgepackt?
Sieht also ungefähr so aus:
template <typename T> auto move(T&& x) noexcept -> typename std::remove_reference<T>::type&& { return static_cast<typename std::remove_reference<T>::type&&>( x ); }
-
Okay, braucht man doch dann nur um bei einer Klasse den Move Konstruktor sicher aufrufen zu können?
MfG
Edit: Weiß ich nicht

-
Warum definiert man move nicht eigentlich einfach so?
template <typename T> T&& move(T& x) { return static_cast<T&&>(x): }Dass es dann mit RValues nicht funktioniert, interessiert doch keinen.
-
Kellerautomat schrieb:
Warum definiert man move nicht eigentlich einfach so?
template <typename T> T&& move(T& x) { return static_cast<T&&>(x): }Dass es dann mit RValues nicht funktioniert, interessiert doch keinen.
Dann geht aber doch z.B. das hier nicht mehr, oder?
string str = "123"; int z; istringstream(str) >> z;
-
Wo ist hier ein move?
-
Kellerautomat schrieb:
Warum definiert man move nicht eigentlich einfach so?
template <typename T> T&& move(T& x) { return static_cast<T&&>(x): }Dass es dann mit RValues nicht funktioniert, interessiert doch keinen.
Das hatte ich mich auch mal gefragt; denn es ist ja sogar eher schädlich, move auf einem Rvalue aufzurufen, da dadurch copy elision unterdrückt wird. Aber ich kann mir vorstellen, dass man in manch generischem Code nicht immer weiß, ob ein Funktionsaufruf (z.B. eine Iterator-Dereferenzierung) ein Lvalue oder Rvalue-Ausdruck ist. Man würde ein Lvalue erwarten, aber es gibt ja auch Iteratoren von "lazy sequences" die den Wert erst dann ausrechnen, wenn man ihn haben will und das ist dann ggf. ein temporäres Objekt...
-
krümelkacker schrieb:
denn es ist ja sogar eher schädlich, move auf einem Rvalue aufzurufen, da dadurch copy elision unterdrückt wird.
Wie das? Kannst du das näher erläutern?
-
yihaaa schrieb:
Was ist der Unterschied zwischen diesen beiden Funktionen?
void func1( int&& ); // #1 void func2( int& ); // #2Gut, dass der Parameter bei #1 eine "Rvalue-Referenz" und bei #2 eine "Lvalue-Referenz" ist, ist inzwischen klar. Ist die Bedeutung denn eigentlich klar?
Eine wichtige Eigenschaft von Rvalue-Referenzen ist: Sie lassen sich nur so initialisieren, dass sie auf etwas verweisen, für das sich kein anderer mehr interessiert. Und da besteht der große Unterschied zwischen R/Lvalue-Referenzen. Die Funktion #1 darf also einfach das int-Objekt ändern, ohne dass es jemanden stören würde; denn so
int i = 23; func1(i);kann man sie auch gar nicht versehentlich aufrufen. Das gibt nämlich einen Compile-Fehler. func1 kann man aber so aufrufen:
int i = 23; func1(i+1); // Referenz wird sich nur auf ein temporäres Objekt beziehen. func1(std::move(i)) // Referenz wird sich auf i beziehenIm letzten Fall ist das aber wieder OK, weil wir hier explizit durch std::move sagen "der Wert von i interessiert uns nicht mehr".
Praktisch wird das dann hauptsächlich bei Move-Konstruktoren. Die nehmen statt "const ObjektTyp&", wie es ein Kopier-Konstruktor tun würde, eben eine Rvalue-Referenz auf nicht const, "ObjektTyp&&". Und weil diese Referenz sich nur auf etwas beziehen kann, was keinen anderen mehr interessiert, darf der Move-Konstruktor das Quell-Objekt verändern. Bei manchen Typen lohnt sich so etwas, weil man den Move-Ctor sehr effizient implementieren kann. Aber auch an anderen Stellen sind diese Referenzen praktisch. Bei vector wird push_back beispielsweise so überladen:
template<class T, class Alloc> class vector { ... void push_back(T const& x); void push_back(T && x); ... };wobei die erste Version das Objekt x in den Vektor kopiert und die zweite Version versucht, das Objekt in den Vektor zu "moven", indem std::move(x) als Konstruktor-Argument genommen wird. Falls es keinen Move-Konstruktor gibt, wird dann eben dank der Regeln der Überladungsauflösung der Kopier-Konstruktor genommen.
std::move sollte man allerdings nicht dann einsetzen, wenn es überflüssig wäre. Überflüssig wäre es, wenn der Ausdruck sowieso schon ein Rvalue ist oder wenn man eine funktionslokale Variable per return zurück geben will:
int quelle(); ... func1(quelle()); // klappt ohne std::move, quelle() ist schon ein Rvalue ...string dings() { string x; ... return x; // std::move ist hier nicht nötig sondern eher schädlich (*) }(* weil dadurch die "return value optimization" ausgeschaltet würde)
kk