NRVO or not?
-
-
@sewing sagte in NRVO or not?:
auto foo(){ Widget w1, w2; ... return std::pair{std::ref(w1),std::cref(w2)}; }
funktioniert ja auch ohne jegliches kopieren
Überleg nochmal. Wie sieht es nun mit der Gültigkeit der Objekte aus?
Was spricht denn hier gegen? Wenn du dir in der Funktion am Anfang nicht sicher bist, welche Objekte du zurück gibst, dann tut's doch auch ein move in das pair hinein, oder nicht?
std::pair<Widget, Widget> foo() { Widget w1,w2, w3, w4; // wir wählen jetzt 2 davon aus return { std::move(w2), std::move(w3) }; }
-
Klar, wenn ich Referenzen auf temporaries nehme, ist das schlecht, aber auf der Call Side wird ja mit nem structured binding by value gearbeitet.
Daher müsste das ja funktionieren oder?
-
@sewing sagte in NRVO or not?:
Klar, wenn ich Referenzen auf temporaries nehme, ist das schlecht, aber auf der Call Side wird ja mit nem structured binding by value gearbeitet.
Daher müsste das ja funktionieren oder?Sobald du die Funktion verlässt, sind die Objekte kaputt. Da hilft auch keine Value Zuweisung auf Caller Seite. Im Gegenteil, genau das ist dann UB (zumindest im Pointer-Fall; dangling Referenzen sind ja per se UB).
Zumal diese Value Zuweisung auch nur für dein std::ref Object gilt. Wie willst du denn direkt an das darunter liegende Objekt ran kommen?
-
Danke vielmals. Ich finde mit copy elision ist es manchmal nicht so einfach zu erkennen, wann was gilt.
RVO funktioniert ja auch bei Verzweigungen innerhalb einer Funktion mit mehreren returns. Allerdings muss in jedem Zweig die gleiche Instanz zurückgegeben werden, also
auto foo() { Widget w; if (false) //do sth. with w return w; else // do sth. else with w return w; }
Wie verhält es sich aber bei einem optional als rückgabewert?
std::optional<Widget> foo() { Widget w; if (false) return std::nullopt; else return std::make_optional(w); }
Offensichtlich werden hier ja zwei unterschiedliche Objekte zurückgegeben, je nach Bedingung
-
Was möchtest du wissen? Du weißt doch schon, dass stets die gleiche Instanz zurück gegeben werden muss.
-
das bedeutet, dass ich bei der realistischen Anwendung von optional für die maybe Rückgabe eines Objekts durch eine Funktion immer mindestens einen move habe - trotz RVO
-
make_optional forwarded an den Widget-Constructor. Wenn du nicht w, sondern die Widget-Konstruktor-Argumente übergibst, wird inplace konstruiert. Geht natürlich nur sinnvoll, wenn du w nicht vorher erzeugt hast.
Und in deinem Code kannst du w moven statt zu kopieren.
-
Interessant finde ich dahingehend auch, dass
#include <iostream> #include <optional> struct Widget { Widget() { std::cerr << "Default ctor" << std::endl; } Widget(int i) { std::cerr << "custom ctor" << std::endl; } Widget(Widget const &) { std::cerr << "copy ctor" << std::endl; } Widget(Widget &&) { std::cerr << "move ctor" << std::endl; } Widget &operator=(Widget const &) { std::cerr << "copy assign" << std::endl; } Widget &operator=(Widget &&) { std::cerr << "move assign" << std::endl; } int i_; }; std::optional<Widget> foo() { // auto o = std::make_optional<Widget>(1); // custom ctor und move ctor auto o = std::make_optional(1); // custom ctor if (false) return std::nullopt; else return o; } int main(){ auto f = foo(); }
nicht moved, aber die auskommentierte Variante mit dem expliziten template Argument noch einen move ausführt.
es kommt doch der overload
template< class T, class... Args > constexpr std::optional<T> make_optional( Args&&... args );
zum tragen hier, wobei der erste template Parameter explizit angegeben wird. Wieso dann der Unterschied?
Klar findet hier eine implizite Konvertierung statt von einem std::optional<int> zu einem optional<Widget> beim return, aber dennoch kein move, was mich wundert
-
Ich würde mal darauf tippen, dass der compiler hier nicht optimieren darf, weil der Move CTor Sideeffects hat.