Ich habe eine Klasse mit copy & amp; move ctor gelöscht.
%Vor%Und ich möchte ein Tupel mit Objekten dieser Klasse erstellen. Aber Folgendes kompiliert nicht:
%Vor%Auf der anderen Seite kompiliert das folgende , gibt aber eine überraschende Ausgabe.
%Vor%Ausgabe
%Vor%Dies bedeutet, dass dtor für Objekte aufgerufen wird, sobald die Tupelkonstruktionslinie endet. Also, was ist die Bedeutung eines Tupels von zerstörten Objekten?
Das ist schlecht:
%Vor% Sie erstellen tuple
of rvalue-Referenzen auf Provisorien, die am Ende des Ausdrucks zerstört werden, sodass Ihnen nicht mehr benötigte Referenzen zur Verfügung stehen.
Die korrekte Aussage wäre:
%Vor% Bis vor kurzem wurde das oben Genannte jedoch vom Standard nicht unterstützt. In N4296 lautet die Formulierung um den relevanten Konstruktor für tuple
[tuple.cnstr]:
%Vor%Benötigt :
sizeof...(Types) == sizeof...(UTypes)
.is_constructible<Ti, Ui&&>::value
ist wahr für allei
.
Effekte : Initialisiert die Elemente im Tupel mit dem entsprechenden Wert instd::forward<UTypes>(u)
.
Anmerkung : Dieser Konstruktor soll nicht teilnehmen in Überladungsauflösung wenn nicht jeder Typ inUTypes
ist implizit in den entsprechenden Typ inTypes
umwandelbar.
Dieser Konstruktor war also nicht an der Überladungsauflösung beteiligt, weil int
nicht implizit in A
konvertiert werden kann. Dies wurde durch die Übernahme von Verbesserung von pair
und tuple
behoben. , die genau Ihren Anwendungsfall adressiert:
Die neue Formulierung für diesen Konstruktor lautet ab N4527:
Hinweise : Dieser Konstruktor darf nicht an der Überladungsauflösung teilnehmen, wenn
sizeof...(Types) >= 1
undis_constructible<Ti, Ui&&>::value
für allei
wahr ist. Der Konstruktor ist genau dann explizit wennis_convertible<Ui&&, Ti>::value
false
für mindestens ein i ist.
Und is_constructible<A, int&&>::value
ist wahr.
Um den Unterschied auf andere Weise darzustellen, hier eine extrem abgespeckte Tupel-Implementierung:
%Vor% Wenn USE_OLD_RULES
definiert ist, ist der erste Konstruktor der einzige ausführbare Konstruktor und daher wird der Code nicht kompiliert, da D
nicht kopierbar ist. Andernfalls ist der zweite Konstruktor der beste mögliche Kandidat und dieser ist wohlgeformt.
Die Einführung war so aktuell, dass weder gcc 5.2 noch clang 3.6 dieses Beispiel noch kompilieren werden. Sie benötigen also entweder einen neueren Compiler (gcc 6.0 funktioniert) oder ein anderes Design.
Ihr Problem ist, dass Sie explizit nach einem Tupel von rvalue-Referenzen gefragt haben, und eine rvalue-Referenz ist nicht weit von einem Zeiger entfernt.
So erstellt auto q = std::tuple<A&&,A&&>(A{100},A{200});
zwei A-Objekte, nimmt (rvalue) Referenzen auf sie, erstellt das Tupel mit den Referenzen ... und zerstört die temporären Objekte, so dass Sie zwei freie Referenzen erhalten.
Selbst wenn es gesagt wird, dass es sicherer ist als das gute alte C und seine ungeeigneten Zeiger, erlaubt C ++ Programmierern immer noch, falsche Programme zu schreiben.
Wie auch immer, das Folgende wäre sinnvoll (beachten Sie die Verwendung von A & amp; und nicht A & amp; & amp;;):
%Vor%