Wenn Sie bei get
suchen, für std::tuple
die Helferfunktion, wird die folgende Überlastung feststellen:Warum Helfer von std :: tuple Rückkehr rvalue Referenz bekommt statt Wert
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get(tuple<Types...>&& t);
Mit anderen Worten, es gibt eine rvalue-Referenz, wenn das Eingabetupel selbst eine rvalue-Referenz ist. Warum nicht als Wert zurückgeben und im Funktionsblock move
aufrufen? Mein Argument ist wie folgt: Die Rückgabe von get wird entweder an eine Referenz oder an einen Wert gebunden (es könnte an nichts gebunden sein, aber das sollte kein üblicher Anwendungsfall sein). Wenn es an einen Wert gebunden ist, wird trotzdem eine Move-Konstruktion stattfinden. Du verlierst also nichts, indem du nach Wert zurückkehrst. Wenn Sie an eine Referenz binden, kann die Rückgabe einer R-Referenz tatsächlich unsicher sein. Um ein Beispiel zu zeigen:
struct Hello {
Hello() {
std::cerr << "Constructed at : " << this << std::endl;
}
~Hello() {
std::cerr << "Destructed at : " << this << std::endl;
}
double m_double;
};
struct foo {
Hello m_hello;
Hello && get() && { return std::move(m_hello); }
};
int main() {
const Hello & x = foo().get();
std::cerr << x.m_double;
}
Wenn er gestartet wird, dieses Programm druckt:
Constructed at : 0x7ffc0e12cdc0
Destructed at : 0x7ffc0e12cdc0
0
Mit anderen Worten, x ist sofort eine baumelnden Referenz. Wenn Sie nur foo wie folgt geschrieben haben:
struct foo {
Hello m_hello;
Hello get() && { return std::move(m_hello); }
};
Dieses Problem tritt nicht auf. Außerdem, wenn Sie dann foo wie folgt verwenden:
Hello x(foo().get());
Es scheint nicht, wie es einen zusätzlichen Aufwand, ob Sie als Wert zurückgeben, oder rvalue Referenz. Ich habe Code wie diesen getestet, und es scheint, als würde er ganz konsequent nur eine einzige Move-Konstruktion ausführen. Z.B. wenn ich Mitglied hinzufügen:
Hello(Hello &&) { std::cerr << "Moved" << std::endl; }
Und ich x-Konstrukt, wie oben, mein Programm druckt nur „verschoben“ einmal unabhängig davon, ob ich nach Wert oder rvalue Referenz zurück.
Gibt es einen guten Grund, dass ich vermisse, oder ist das ein Versehen?
Hinweis: Es gibt eine gute verwandte Frage hier: Return value or rvalue reference?. Es scheint so zu sein, dass Value Return im Allgemeinen in dieser Situation vorzuziehen ist, aber die Tatsache, dass es in der STL auftaucht, macht mich neugierig, ob die STL diese Argumentation ignoriert hat oder ob sie besondere Gründe hat, die möglicherweise nicht zutreffend sind allgemein.
Bearbeiten: Jemand hat vorgeschlagen, diese Frage ist ein Duplikat von Is there any case where a return of a RValue Reference (&&) is useful?. Das ist nicht der Fall; Diese Antwort schlägt vor, dass die Rückgabe von R-Werten als eine Möglichkeit zum Kopieren von Datenelementen verwendet wird. Wie ich oben ausführlich besprochen habe, wird das Kopieren weggelassen, ob Sie nach Wert oder R-Wert-Referenz zurückgeben, vorausgesetzt, Sie rufen zuerst move
.
Dies ist nicht entfernt ein Duplikat. Meine Antwort beschreibt explizit und im Detail, wie die Rückgabe nach Wert und das Aufrufen von std :: move im Voraus den gleichen Effekt des Verschiebens von einem Datenelement erzielt. Meine Frage bezieht sich auf die Unterschiede in den beiden Ansätzen. Die Frage, die ich selbst anführte, hängt mehr mit meiner Frage zusammen als mit der, die Sie zitiert haben. Bitte lesen Sie aufmerksamer, bevor Sie etwas als Duplikat bezeichnen. –
Wenn Sie nach Wert zurückgeben, erstellen Sie eine Kopie. Wenn Sie es verschieben, verschieben Sie eine Kopie. Wenn Sie eine r-Wert-Referenz zurückgeben, können Sie sie entweder verschieben, dann wird das ursprüngliche Objekt verschoben oder es wird nicht mit move sematics behandelt, dann verhält es sich wie eine normale Referenz. Der Punkt ist, dass Sie das Kopieren nicht erzwingen, wenn Sie eine r-Wert-Referenz zurückgeben. –
Warum müssen Sie die Konstruierbarkeit erreichen? – Columbo