initializer_list und move-Semantik
Bin ich erlaubt, sich zu bewegen, Elemente aus einem std::initializer_list<T>
?
#include <initializer_list>
#include <utility>
template<typename T>
void foo(std::initializer_list<T> list)
{
for (auto it = list.begin(); it != list.end(); ++it)
{
bar(std::move(*it)); //kosher?
}
}
Seit std::intializer_list<T>
erfordert spezielle compiler und Aufmerksamkeit nicht Wert-Semantik wie normale Container der C++ - standard-Bibliothek, I ' D rather be safe than sorry und Fragen.
Die core-Sprache definiert, die das Objekt bezeichnet, die durch eine
initializer_list<T>
werden nicht-const. Wie initializer_list<int>
bezieht sich auf int
Objekte. Aber ich denke, das ist ein Mangel - es ist vorgesehen, dass der Compiler die statisch zuweisen einer Liste im nur-lese-Speicher.InformationsquelleAutor fredoverflow | 2011-11-19
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nein, das wird nicht so funktionieren wie Sie sollen; Sie werden immer noch kopiert. Ich bin ziemlich überrascht, da ich dachte, dass
initializer_list
Bestand zu halten, eine Reihe von Provisorien, bis Siemove
'd....begin
undend
fürinitializer_list
zurückconst T *
, so das Ergebnis dermove
im code istT const &&
— eine unveränderliche rvalue-Referenz. Ein solcher Ausdruck kann nicht sinnvoll verschoben werden. Es wird eine Bindung an eine Funktion parameter vom TypT const &
weil rvalues tun binden const lvalue Referenzen, und Sie werden noch sehen, copy-Semantik.Wohl der Grund für dieses ist, also kann der compiler entscheiden, um die
initializer_list
eine statisch initialisierte Konstanten, aber es scheint, wäre es sauberer zu machen, seine Artinitializer_list
oderconst initializer_list
bei der compiler nach eigenem Ermessen, so dass die Benutzer nicht wissen, ob zu erwartenconst
oder veränderlich Ergebnis vonbegin
undend
. Aber das ist nur mein Bauchgefühl, wahrscheinlich gibt es einen guten Grund, ich bin falsch.Update: ich geschrieben habe ein ISO-Vorschlag für
initializer_list
Unterstützung von move-only-Typen. Es ist nur ein Erster Entwurf, und es ist nicht überall implementiert, aber Sie können sehen, dass es für die weitere Analyse des Problems.std::move
ist sicher, wenn auch nicht produktiv. (Abgesehen vonT const&&
move-Konstruktoren.)Ich glaube nicht, dass Sie könnte die ganze Argumentation entweder
const std::initializer_list<T>
oder nurstd::initializer_list<T>
in einer Weise, die nicht zu überraschungen ziemlich oft. Bedenken Sie, dass jedes argument in derinitializer_list
kann entwederconst
oder nicht, und dass bekannt ist, in den Kontext des Aufrufers, sondern muss der compiler generiert nur eine version des code im Kontext des aufgerufenen (d.h. im innerenfoo
es nicht wissen, etwas über die Argumente, die der Aufrufer übergeben)Guter Punkt, aber es würde noch nützlich sein, um eine
std::initializer_list &&
überlastung etwas tun, auch wenn ein nicht-Referenz-überladung ist ebenfalls erforderlich. Ich vermute, es wäre noch verwirrender als die jetzige situation, die ist schon schlecht.Es kann nicht gehackt werden um. Ich sehe nicht genau, was der code erreichen soll wrt initializer_list, aber wie der Benutzer Sie nicht die erforderlichen Berechtigungen, um von ihm zu bewegen. Safe-code wird das auch nicht tun.
verspäteter Kommentar, aber was ist der status des Vorschlags. Gibt es eine remote-chance, kann es sinnvoll sein, in C++20?
InformationsquelleAutor Potatoswatter
Nicht in der Weise, dass Sie wollen. Sie kann sich nicht bewegen eine
const
Objekt. Undstd::initializer_list
nurconst
Zugriff auf seine Elemente. Also das der Typit
istconst T *
.Ihrem Versuch zu nennen
std::move(*it)
wird, wird nur in einem l-Wert. SPRICH: eine Kopie.std::initializer_list
Referenzen statische Speicher. Das ist es, was die Klasse ist für. Sie können nicht bewegen von statischen Speicher, denn Bewegung bedeutet Veränderung. Sie können nur kopieren.initializer_list
Referenzen Stapel, wenn das notwendig ist. (Wenn die Inhalte nicht konstant sind, ist es immer noch thread-sicher.).Sie kann sich nicht bewegen von einem Konstanten Objekt. Die
initializer_list
Objekt selbst, kann ein xvalue, aber es ist der Inhalt (die eigentlichen array von Werten, die es-Punkte) sindconst
, weil diese Inhalte können statische Werte. Sie können einfach nicht bewegen aus dem Inhalt derinitializer_list
.Siehe meine Antwort und die Diskussion. Er bewegt den iterator dereferenziert, wodurch ein
const
xvalue.move
könnte bedeutungslos sein, aber es ist legal und auch möglich, um einen parameter zu deklarieren, der akzeptiert, dass nur. Wenn Sie eine bestimmte Art geschieht, ein no-op, es könnte sogar funktionieren.Die C++11-standard, verbraucht er eine Menge von Sprache, sicherzustellen, dass nicht-temporäre Objekte werden nicht verschoben, es sei denn, Sie verwenden
std::move
. Dieser sorgt dafür, dass Sie sagen, von der Inspektion, wenn ein Vorgang passiert, denn es wirkt sich sowohl auf die Quell-und die Ziel - (Sie will nicht, dass es passieren implizit für die benannten Objekte). Deshalb, wenn Siestd::move
in einem Ort, wo ein move-operation nicht geschehen (und keine eigentliche Bewegung wird passieren, wenn Sie haben eineconst
xvalue), dann den code, ist irreführend. Ich denke, es ist ein Fehler, fürstd::move
aufrufbar sein, auf eineconst
Objekt.Vielleicht, aber ich werde immer noch nehmen weniger Ausnahmen von den Regeln über die Möglichkeit der Irreführung code. Aber das ist genau der Grund, warum ich mit "Nein" beantwortet, obwohl es legal ist, und das Ergebnis ist ein xvalue, auch wenn es nur binden als const lvalue. Um ehrlich zu sein, ich hatte bereits einen kurzen Flirt mit
const &&
in ein garbage Collection-Klasse mit den verwalteten Zeiger, wo alles relevante war veränderlich und bewegt den Zeiger Verwaltung, aber hatte keinen Einfluss auf die enthaltenen Wert. Es gibt immer knifflige Grenzfälle :v) .InformationsquelleAutor Nicol Bolas
Diese nicht so funktionieren, wie angegeben, da
list.begin()
Typconst T *
, und es gibt keine Weise, die Sie verschieben können, von einem Konstanten Objekt. Die Sprache, die Designer wahrscheinlich gemacht, dass, damit, um zu ermöglichen, Initialisierer-Listen enthalten, zum Beispiel string-Konstanten, aus denen es unangemessen wäre, sich zu bewegen.Jedoch, wenn Sie in einer situation, wo Sie wissen, dass die Initialisierungsliste enthält rvalue-Ausdrücke (oder Sie wollen erzwingen, dass der Benutzer schreiben diese), dann gibt es einen trick, damit es funktioniert (ich wurde inspiriert durch die Antwort von Sumant für diese, aber die Lösung ist viel einfacher als das). Sie müssen die Elemente gespeichert, die in der initialiser Liste nicht
T
Werte, sondern Werte, die KapselnT&&
. Wenn dann auch noch diejenigen, die Werte selbst sindconst
qualifiziert, können Sie noch immer abrufen einer modifizierbaren rvalue.Nun, anstatt zu erklären, eine
initializer_list<T>
argument, Sie deklarieren eineinitializer_list<rref_capture<T> >
argument. Hier ist ein konkretes Beispiel, mit einem Vektor, derstd::unique_ptr<int>
smart pointers, für die nur move-Semantik eindeutig definiert ist (also diese Objekte selbst können nie abgelegt werden, die in einer Initialisierer-Liste); aber der Initialisierungsliste unten kompiliert ohne Probleme.Einer Frage braucht eine Antwort: wenn die Elemente der Initialisierungsliste sein sollte, true prvalues (im Beispiel sind Sie xvalues), hat die Sprache sicher, dass die Lebensdauer der entsprechenden Provisorien erstreckt sich bis zu dem Punkt, wo Sie verwendet werden? Ehrlich gesagt, ich glaube nicht, dass der entsprechende Abschnitt 8.5 der Norm beschäftigt sich mit diesem Problem überhaupt. Jedoch, Lesen 1.9:10, es scheint, dass die relevanten full-Ausdruck in allen Fällen umfasst die Verwendung der Initialisierungsliste, so denke ich, dass es keine Gefahr von dangling rvalue-Referenzen.
"Hello world"
? Wenn Sie sich von Ihnen, Sie kopieren einfach einen Zeiger (oder die Bindung einer Referenz)."Eine Frage braucht eine Antwort" Die Initialisierungen innerhalb
{..}
gebunden sind, um Referenzen in der Funktion parameter derrref_capture
. Dies gilt nicht verlängern Ihre Lebensdauer, Sie sind immer noch zerstört am Ende den vollständigen äußerung, in der Sie erstellt wurden, haben.Pro T. C.'s Bemerkung aus der anderen Antwort: Wenn Sie mehrere überladungen des Konstruktors, der wickeln Sie die
std::initializer_list<rref_capture<T>>
in einigen Transformations-Eigenschaft Ihrer Wahl - sagen,std::decay_t
- zum blockieren unerwünschter Abzug.InformationsquelleAutor Marc van Leeuwen
Ich dachte, es könnte aufschlussreich sein, um bieten einen guten Ausgangspunkt für einen workaround.
Kommentare inline.
initializer_list
bewegt werden kann, aus, nicht, ob noch jemand workarounds. Außerdem, das wichtigste Verkaufsargument voninitializer_list
ist, dass es nur Vorlagen, die auf den Typ des Elements, nicht auf die Anzahl der Elemente, und daher nicht von den Empfängern verlangen, auch Vorlagen - und dies völlig verliert, die.du hast absolut Recht. Bin ich der Ansicht, dass das gemeinsame wissen in Bezug auf die Frage ist eine gute Sache in sich selbst. In diesem Fall, vielleicht hat es geholfen, die OP und vielleicht ist es gar nicht - er antwortet nicht. Mehr als oft nicht, jedoch das OP und die anderen nehmen extra material im Zusammenhang mit der Frage.
Sicher, es kann tatsächlich Hilfe für Leser, die wollen so etwas wie
initializer_list
aber nicht gelten alle Einschränkungen, die machen es nützlich. 🙂welche Einschränkungen habe ich übersehen?
Alles, was ich meine ist, dass
initializer_list
(per compiler-magic) verhindert, dass template-Funktionen, auf die Anzahl der Elemente, etwas, das grundsätzlich erforderlich, die von alternativen basierend auf arrays und/oder variadic-Funktionen, also dem einschränken der Auswahl von Fällen, wo die letztere nutzbar sind. Von meinem Verständnis, genau das ist eines der wichtigsten Begründungen für dieinitializer_list
, so schien es Wert zu erwähnen.InformationsquelleAutor Richard Hodges
Scheint es nicht erlaubt, in den aktuellen standard als schon beantwortet. Hier ist eine andere Möglichkeit, etwas zu erreichen, ähnlich der, durch die Definition der Funktion als Variable statt einer Initialisierungsliste.
Variadic-Vorlagen verarbeiten kann r-Wert-Referenzen angemessen, im Gegensatz zu initializer_list.
In diesem Beispiel-code, den ich verwendet eine Reihe von kleinen Hilfsfunktionen zum konvertieren der Variable Argumente in einen Vektor, um zu machen es ähnlich wie das original-code. Aber natürlich, Sie können schreiben Sie eine rekursive Funktion mit variadic templates direkt statt.
initializer_list
bewegt werden kann, aus, nicht, ob noch jemand workarounds. Außerdem, das wichtigste Verkaufsargument voninitializer_list
ist, dass es nur Vorlagen, die auf den Typ des Elements, nicht auf die Anzahl der Elemente, und daher nicht von den Empfängern verlangen, auch Vorlagen - und dies völlig verliert, die.InformationsquelleAutor Hiroshi Ichikawa
Betrachten die
in<T>
idiom beschrieben auf cpptruths. Die Idee ist, um zu bestimmen, lvalue/rvalue zur Laufzeit und rufen Sie dann verschieben oder kopieren-Bau.in<T>
erkennt rvalue/lvalue, obwohl die standard-Schnittstelle, initializer_list ist const-Referenz.Bitte Lesen Sie den blog und hinterlassen Sie mir einen Kommentar, wenn Sie nicht einverstanden sind oder eine bessere alternative. Selbst wenn der compiler kennt den Wert der Kategorie, initializer_list nicht beibehalten, weil es nur const-Iteratoren. So müssen Sie "capture" Wert der Kategorie, wenn Sie konstruieren die initializer_list und passieren Sie durch, damit Sie die Funktion nutzen können, wie es ihm gefällt.
Diese Antwort ist im Grunde nutzlos, ohne die folgenden link, und SO Antworten die nützlich sein sollten, ohne die folgenden links.
mein Kommentar aus einem identischen post anderswo] Bedeutet, dass gewaltig Durcheinander tatsächlich keine messbare Vorteile für die performance oder Speicherauslastung, und wenn dem so ist, eine hinreichend große Menge solcher Vorteile angemessen ausgeglichen, wie schrecklich es aussieht und die Tatsache, dass es dauert über eine Stunde, um herauszufinden, was er zu tun versucht? Ich irgendwie Zweifel.
InformationsquelleAutor Sumant