return by value - deep/shallow copy
-
Ne, nix. Mit C++11 Move-Konstruktoren ist das Problem doch längst gelöst, wundert mich, dass das noch niemand erwähnt hat. oO
-
Danke für deine Antwort

Kann man es denn irgendwie bewerkstelligen, dass bei der return-Anweisung gemoved wird anstatt kopiert, d.h. dass der move-Konstruktor verwendet wird anstatt der copy-Konstruktor?
-
Das passiert automatisch, sofern ein Move-Konstruktor vorhanden ist.
-
Kann man dann zusammenfassend festhalten, dass wenn man eine lokale Variable returned automatisch der move-Konstruktor gewählt wird, sofern er vorhanden ist (ansonsten der copy-Konstruktor) und wenn eine globale Variable returned wird stets der copy-Konstruktor?
Gruß

-
Ne, es gibt auch noch statische Variablen etc.
Es wird immer kopiert, es sei denn durch das return statement wird die Lebenszeit zurückgegebenen Objekts beendet, dann wird gemoved. Irgendwie logisch.
-
Ja stimmt

Vielen Dank

-
Nein das stimmt so nicht. Die Ausgabe dieses Programms
#include <iostream> class A { public: A() { std::cout << "default" << std::endl; } A( const A& ) { std::cout << "copy" << std::endl; } A( A&& ) { std::cout << "move" << std::endl; } ~A() { std::cout << "destruction" << std::endl; } }; A foo() { A a; return a; } int main() { A a = foo(); }Ist bei mir beispielsweise nur
default destructionDer compiler konstruiert das A aus foo direkt dorthin, wo es nach der Funktion gebraucht wird, d.h. es wird nie kopiert. Stattdessen übergibt main() foo() implizit einen return_ptr (oder wie man das auch nennen will), wohin das Objekt konstruiert werden soll, d.h. auf den Stackbereich von main(). Dadurch kann foo() einfach returnen und braucht a gar nicht zu kopieren oder zu moven.
Hier ist sowas nicht möglich, da je nach Pfad ein anderes Objekt returned werden kann (zumindest moved mein gcc hier):A foo() { A a; A b; if( true ) return a; else return b; }Ich meine diese Optimierung heißt 'return value optimization', danach kannst du mal googlen. Es gibt auch einige gute Tutorials zum Thema move, z.B. 'moving forward'.
-
Danke für deine Hilfe, bekommt man diese Ausgabe auch, wenn man alle Optimierungen deaktiviert?
-
Nein, RVO wird üblicherweise nur im Releasemodus durchgeführt. Du bekommst also im Debugmodus eine andere Ausgabe.
-
Bei mir schon, ja. Ich weiß aber nicht, ob diese Optimierung verpflichtend im Standard festgelegt ist, oder ob man auch standardkonform immer kopieren/moven darf.
-
Sie wird wohl nicht verpflichtend sein, wenn im Debugmodus die "normale" Ausgabe kommt.

-
Der Debug-Modus wird beim GCC mit -g eingeleitet oder? Bei mir ändert sich da nichts an der Ausgabe

-
Kann sein, dass GCC das immer an hat.
-
Ich bekomme aber die Ausgabe (mit oder ohne -g), die eigentlich im Release-Modus erscheinen sollte:
default
destruction
-
Sag ich doch. GCC macht RVO scheinbar einfach immer, warum auch nicht?
-
Daran soll's nicht scheitern.
#include <cstdlib> #include <iostream> struct A { A() { std::cout << "default" << std::endl; } A(A const &) { std::cout << "copy" << std::endl; } A(A &&) { std::cout << "move" << std::endl; } ~A() { std::cout << "destruction" << std::endl; } }; A foo() { A a, b; // RVO verhindern. return std::rand() % 2 == 0 ? a : b; } int main() { A a = foo(); }gibt bei mir
default default copy destruction destruction destructionMove gibt's mit
return std::move(std::rand() % 2 == 0 ? a : b);a in main wird allerdings von gcc nach wie vor direkt initialisiert. Grundlage ist g++ 4.7.0 mit -std=c++11.
-
MatheStein schrieb:
Der Debug-Modus wird beim GCC mit -g eingeleitet oder? Bei mir ändert sich da nichts an der Ausgabe

-g erzeugt nur Debug-Symbole.
-O0 setzt die Optimierungen auf 0, also Debug-Build. -O1, -O2, -O2.5, -Os und -O3 erzeugen Realese-Builds.
-
Bei mir bleibt die Ausgabe stets gleich, auch mit -O0
-
Kann man es eigentlich irgendwie erzwingen, dass bei return anstatt gemoved wird kopiert wird?
Habe hier derzeit eine Bib, die evtl. schlecht programmiert ist.
Ich denke das Objekt, dass gemoved wird gibt seinen Speicher frei, obwohl es diese an das neue Objekt abgeben sollte, deshalb wollte ich das moven erstmal umgehen.Gruß

-
Die Verwendung der Begriffe "deep" und "shallow" bzgl Default-Copy-Ctor finde ich irgendwie wenig sinnvoll. Es wird einfach elementweise kopiert. Was das bedeutet, hängt natürlich von den Typen ab. Beispiel:
struct dings { std::vector<int> elem; }Obwohl die sizeof(elem) Bytes hier nur für einen Zeiger und ein paar Ints verwendet werden, referenziert der Zeiger die Vektor-Elemente, die natürlich, wenn ein dings-Objekt kopiert wird, mitkopiert werden ...
MatheStein schrieb:
Kann man es eigentlich irgendwie erzwingen, dass bei return anstatt gemoved wird kopiert wird?
Bestimmt:
... template<class T> T const& constify(T& x) {return x;} string GCC_macht_hier_NRVO() // eine Form der "copy elision" { string r = "hello"; return r; } string move_erzwingen() { string r = "hello"; return std::move(r); // blöde Idee, da NRVO natürlich besser ist als ein move } string copy_erzwingen() { string r = "hello"; return constify(r); // noch blödere Idee, da hier unnötig kopiert wird } ...Die "copy elisions", die der Standard erlaubt, kann man sonst beim GCC über einen speziellen Schalter deaktivieren. Bin gerade zu faul, nachzugucken, wie der heißt.
Ich möchte noch darauf hinweisen, dass das const im letzten Fall gar nicht der springende Punkt ist, sondern, dass hier eine Funktion eine Referenz zurückliefert. Hier darf und kann kein RVO angewandt werden. Wenn man r nur als const string deklarieren würde, wäre RVO immer noch machbar, ein move natürlich nicht mehr.