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 vectorin kopiert bar.
In C ++ 11 wird stattdessen das bar::datas verschoben, was grundsätzlich kostenlos ist.
In diesem Fall hängt dies von Optimierungen im stdContainer ab vector. In allen folgenden Fällen werden stdContainer nur verwendet, weil es sich um C ++ - Objekte handelt, die movein C ++ 11 "automatisch" eine effiziente Semantik aufweisen, wenn Sie Ihren Compiler aktualisieren. Objekte, die es nicht blockieren und einen stdContainer enthalten, erben auch die automatisch verbesserten moveKonstruktoren.
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 returnStandorts 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 whatevererstellt und dann in den Vektor kopiert v. Es werden 2 std::stringPuffer mit jeweils identischen Daten zugewiesen und einer verworfen.
In C ++ 11 wird eine temporäre whateverDatei erstellt. Die whatever&& push_backÜberlastung ist dann moveso temporär in den Vektor v. Ein std::stringPuffer wird zugewiesen und in den Vektor verschoben. Ein Leerzeichen std::stringwird 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_functionein 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 stdContainern, synthetisiert jedoch keine Verschiebungskonstruktoren für Ihre Typen.
Typen, die std::vectors 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).