In C ++ 11 können wir diesen Code schreiben:
struct Cat {
Cat(){}
};
const Cat cat;
std::move(cat); //this is valid in C++11
Wenn ich anrufe std::move
, bedeutet dies, dass ich das Objekt verschieben möchte, dh ich werde das Objekt ändern. Das Verschieben eines const
Objekts ist unvernünftig. Warum wird std::move
dieses Verhalten nicht eingeschränkt? Es wird in Zukunft eine Falle sein, oder?
Hier bedeutet Falle, wie Brandon im Kommentar erwähnt hat:
"Ich denke, er meint, es" fängt "ihn hinterhältig hinterhältig, denn wenn er es nicht merkt, erhält er eine Kopie, die nicht das ist, was er beabsichtigt hat."
In dem Buch 'Effective Modern C ++' von Scott Meyers gibt er ein Beispiel:
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text)) //here we want to call string(string&&),
//but because text is const,
//the return type of std::move(text) is const std::string&&
//so we actually called string(const string&)
//it is a bug which is very hard to find out
private:
std::string value;
};
Wenn std::move
es verboten war, an einem const
Objekt zu arbeiten, konnten wir den Fehler leicht herausfinden, oder?
std::move
an sich macht nichts mit dem Objekt. Man könnte argumentieren,std::move
ist schlecht benannt.CAT cat2 = std::move(cat);
unter der Annahme, dassCAT
die regelmäßige Zuweisung von Bewegungen unterstützt wird.std::move
ist nur eine Besetzung, es bewegt eigentlich nichtsAntworten:
hier sehen wir eine Verwendung von
std::move
auf einemT const
. Es gibt a zurückT const&&
. Wir haben einen Verschiebungskonstruktorstrange
, der genau diesen Typ annimmt.Und es heißt.
Nun ist es wahr, dass dieser seltsame Typ seltener ist als die Fehler, die Ihr Vorschlag beheben würde.
Andererseits
std::move
funktioniert das Vorhandene im generischen Code besser, wenn Sie nicht wissen, ob der Typ, mit dem Sie arbeiten, aT
oder a istT const
.quelle
std::move
const
const T&&
. Dies drückt ein "API-Protokoll" der Art "Ich werde einen rvalue-ref nehmen, aber ich verspreche, dass ich ihn nicht ändern werde" aus. Ich denke, abgesehen von der Verwendung von veränderlichen, ist es ungewöhnlich. Möglicherweise besteht ein anderer Anwendungsfall darin,forward_as_tuple
fast alles verwenden und später verwenden zu können.Hier übersehen Sie einen Trick,
std::move(cat)
der eigentlich nichts bewegt . Es weist den Compiler lediglich an, zu versuchen, sich zu bewegen. Da Ihre Klasse jedoch keinen Konstruktor hat, der a akzeptiertconst CAT&&
, verwendet sie stattdessen den implizitenconst CAT&
Kopierkonstruktor und kopiert sicher. Keine Gefahr, keine Falle. Wenn der Kopierkonstruktor aus irgendeinem Grund deaktiviert ist, wird ein Compilerfehler angezeigt.druckt
COPY
nichtMOVE
.http://coliru.stacked-crooked.com/a/0dff72133dbf9d1f
Beachten Sie, dass der Fehler in dem von Ihnen erwähnten Code ein Leistungsproblem und kein Stabilitätsproblem ist, sodass ein solcher Fehler niemals zu einem Absturz führen wird. Es wird nur eine langsamere Kopie verwendet. Darüber hinaus tritt ein solcher Fehler auch bei nicht konstanten Objekten auf, die keine Verschiebungskonstruktoren haben. Wenn Sie also nur eine
const
Überladung hinzufügen, werden nicht alle erfasst. Wir könnten prüfen, ob das Konstrukt oder die Zuweisung vom Parametertyp verschoben werden kann, dies würde jedoch den generischen Vorlagencode beeinträchtigen, der auf den Kopierkonstruktor zurückgreifen soll . Und zum Teufel, vielleicht möchte jemand in der Lage sein zu konstruieren, vonconst CAT&&
wem soll ich sagen, dass er es nicht kann?quelle
const
Nichtwert benötigt, ebenfalls nicht hilft. [class.copy] §8: "Andernfalls hat der implizit deklarierte Kopierkonstruktor die FormX::X(X&)
"Ein Grund, warum die restlichen Antworten bisher übersehen wurden, ist die Fähigkeit des generischen Codes, angesichts von Bewegungen stabil zu sein. Nehmen wir zum Beispiel an, ich wollte eine generische Funktion schreiben, die alle Elemente aus einer Art von Container verschiebt, um eine andere Art von Container mit denselben Werten zu erstellen:
Cool, jetzt kann ich relativ effizient ein
vector<string>
aus einem erstellendeque<string>
und jeder Einzelnestring
wird dabei bewegt.Aber was ist, wenn ich von einem wechseln möchte
map
?Wenn
std::move
auf einem Nichtargument bestanden wirdconst
, wird die obige Instanziierung vonmove_each
nicht kompiliert, da versucht wird, einconst int
(daskey_type
vonmap
) zu verschieben. Aber diesem Code ist es egal, ob er das nicht bewegen kannkey_type
. Es möchte dasmapped_type
(std::string
) aus Leistungsgründen verschieben.Für dieses Beispiel und unzählige andere Beispiele wie dieses in der generischen Codierung
std::move
ist es eine Aufforderung zum Verschieben , keine Aufforderung zum Verschieben.quelle
Ich habe die gleichen Bedenken wie das OP.
std :: move bewegt kein Objekt und garantiert auch nicht, dass das Objekt beweglich ist. Warum heißt es dann bewegen?
Ich denke, nicht beweglich zu sein kann eines der folgenden zwei Szenarien sein:
1. Der Bewegungstyp ist const.
Der Grund, warum wir das Schlüsselwort const in der Sprache haben, ist, dass der Compiler jede Änderung an einem Objekt verhindern soll, das als const definiert ist. Angesichts des Beispiels in Scott Meyers 'Buch:
Was bedeutet es wörtlich? Verschieben Sie eine const-Zeichenfolge in das Wertelement - zumindest verstehe ich das, bevor ich die Erklärung lese.
Wenn die Sprache beabsichtigt, keine Verschiebung durchzuführen oder nicht zu garantieren, dass die Verschiebung beim Aufruf von std :: move () anwendbar ist, ist dies bei Verwendung der Wortverschiebung buchstäblich irreführend.
Wenn die Sprache Menschen, die std :: move verwenden, zu einer besseren Effizienz ermutigt, muss sie solche Fallen so früh wie möglich verhindern, insbesondere bei dieser Art von offensichtlichen wörtlichen Widersprüchen.
Ich bin damit einverstanden, dass die Leute sich bewusst sein sollten, dass das Verschieben einer Konstante unmöglich ist, aber diese Verpflichtung sollte nicht bedeuten, dass der Compiler schweigen kann, wenn offensichtliche Widersprüche auftreten.
2. Das Objekt hat keinen Verschiebungskonstruktor
Persönlich denke ich, dass dies eine andere Geschichte ist als OPs Besorgnis, wie Chris Drew sagte
quelle
Ich bin überrascht, dass niemand den Aspekt der Abwärtskompatibilität erwähnt hat. Ich glaube,
std::move
wurde absichtlich dafür in C ++ 11 entwickelt. Stellen Sie sich vor, Sie arbeiten mit einer älteren Codebasis, die stark von C ++ 98-Bibliotheken abhängt. Ohne den Fallback bei der Kopierzuweisung würde das Verschieben also Probleme verursachen.quelle
Glücklicherweise können Sie die Prüfung von clang-tidy verwenden, um solche Probleme zu finden: https://clang.llvm.org/extra/clang-tidy/checks/performance-move-const-arg.html
quelle