Mir sind 5 allgemeine Kategorien bekannt, in denen das Neukompilieren eines C ++ 03-Compilers als C ++ 11 zu unbegrenzten Leistungssteigerungen führen kann, die praktisch nichts mit der Qualität der Implementierung zu tun haben. Dies sind alles Variationen der Bewegungssemantik.
std::vector
neu zuweisen
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
Jedes Mal foo
, wenn der Puffer in C ++ 03 neu zugewiesen wird, wird jeder vector
in kopiert bar
.
In C ++ 11 wird stattdessen das bar::data
s verschoben, was grundsätzlich kostenlos ist.
In diesem Fall hängt dies von Optimierungen im std
Container ab vector
. In allen folgenden Fällen werden std
Container nur verwendet, weil es sich um C ++ - Objekte handelt, die move
in C ++ 11 "automatisch" eine effiziente Semantik aufweisen, wenn Sie Ihren Compiler aktualisieren. Objekte, die es nicht blockieren und einen std
Container enthalten, erben auch die automatisch verbesserten move
Konstruktoren.
NRVO-Fehler
Wenn NRVO (Named Return Value Optimization) fehlschlägt, wird in C ++ 03 beim Kopieren zurückgegriffen, in C ++ 11 beim Verschieben. Ausfälle von NRVO sind einfach:
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
oder auch:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
Wir haben drei Werte - den Rückgabewert und zwei verschiedene Werte innerhalb der Funktion. Mit Elision können die Werte innerhalb der Funktion mit dem Rückgabewert 'zusammengeführt' werden, jedoch nicht miteinander. Sie können beide nicht mit dem Rückgabewert zusammengeführt werden, ohne miteinander zusammengeführt zu werden.
Das Grundproblem besteht darin, dass die NRVO-Elision fragil ist und Code mit Änderungen, die sich nicht in der Nähe des return
Standorts befinden, an dieser Stelle plötzlich massive Leistungseinbußen aufweisen kann, ohne dass eine Diagnose ausgegeben wird. In den meisten NRVO-Fehlerfällen endet C ++ 11 mit a move
, während C ++ 03 mit einer Kopie endet.
Rückgabe eines Funktionsarguments
Elision ist auch hier nicht möglich:
std::set<int> func(std::set<int> in){
return in;
}
In C ++ 11 ist dies billig: In C ++ 03 gibt es keine Möglichkeit, die Kopie zu umgehen. Argumente für Funktionen können nicht mit dem Rückgabewert entfernt werden, da die Lebensdauer und Position des Parameters und des Rückgabewerts vom aufrufenden Code verwaltet werden.
C ++ 11 kann jedoch von einem zum anderen verschoben werden. (In einem weniger spielerischen Beispiel könnte etwas mit dem gemacht werden set
).
push_back
oder insert
Schließlich findet keine Elision in Container statt: C ++ 11 überlastet jedoch rvalue move insert-Operatoren, wodurch Kopien gespeichert werden.
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
In C ++ 03 wird ein Temporär whatever
erstellt und dann in den Vektor kopiert v
. Es werden 2 std::string
Puffer mit jeweils identischen Daten zugewiesen und einer verworfen.
In C ++ 11 wird eine temporäre whatever
Datei erstellt. Die whatever&&
push_back
Überlastung ist dann move
so temporär in den Vektor v
. Ein std::string
Puffer wird zugewiesen und in den Vektor verschoben. Ein Leerzeichen std::string
wird verworfen.
Zuordnung
Gestohlen aus der Antwort von @ Jarod42 unten.
Elision kann nicht mit Zuweisung erfolgen, sondern mit Abzug.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
Hier wird some_function
ein Kandidat zurückgegeben, von dem entfernt werden soll. Da er jedoch nicht zum direkten Erstellen eines Objekts verwendet wird, kann er nicht entfernt werden. In C ++ 03 führt das Obige dazu, dass der Inhalt des Temporären kopiert wird some_value
. In C ++ 11 wird es verschoben some_value
, was grundsätzlich kostenlos ist.
Für die volle Wirkung des oben genannten benötigen Sie einen Compiler, der Verschiebungskonstruktoren und Zuweisungen für Sie synthetisiert.
MSVC 2013 implementiert Verschiebungskonstruktoren in std
Containern, synthetisiert jedoch keine Verschiebungskonstruktoren für Ihre Typen.
Typen, die std::vector
s und ähnliches enthalten, erhalten solche Verbesserungen in MSVC2013 nicht, werden sie jedoch in MSVC2015 erhalten.
clang und gcc haben längst implizite Verschiebungskonstruktoren implementiert. Der 2013er Compiler von Intel unterstützt die implizite Generierung von Verschiebungskonstruktoren, wenn Sie übergeben -Qoption,cpp,--gen_move_operations
(sie tun dies standardmäßig nicht, um mit MSVC2013 kompatibel zu sein).