Je connais 5 catégories générales où la recompilation d'un compilateur C ++ 03 en C ++ 11 peut entraîner des augmentations de performances illimitées qui ne sont pratiquement pas liées à la qualité de l'implémentation. Ce sont toutes des variantes de la sémantique des mouvements.
std::vector réaffecter
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
chaque fois que le fooest réaffecté tampon de 03 en C ++ , il copié tous vectoren bar.
En C ++ 11, il déplace à la place le bar::datas, qui est fondamentalement gratuit.
Dans ce cas, cela repose sur des optimisations à l'intérieur du stdconteneur vector. Dans tous les cas ci-dessous, l'utilisation de stdconteneurs est simplement parce que ce sont des objets C ++ qui ont une movesémantique efficace en C ++ 11 "automatiquement" lorsque vous mettez à niveau votre compilateur. Les objets qui ne le bloquent pas et qui contiennent un stdconteneur héritent également des moveconstructeurs améliorés automatiques .
Échec NRVO
Lorsque NRVO (nommé l'optimisation de la valeur de retour) échoue, en C ++ 03, il retombe sur copie, sur C ++ 11, il retombe en mouvement. Les échecs de NRVO sont faciles:
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;
}
ou même:
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;
}
Nous avons trois valeurs - la valeur de retour et deux valeurs différentes dans la fonction. Elision permet de «fusionner» les valeurs de la fonction avec la valeur de retour, mais pas entre elles. Ils ne peuvent pas tous les deux être fusionnés avec la valeur de retour sans fusionner les uns avec les autres.
Le problème de base est que l'élision NRVO est fragile, et le code avec des modifications non proches du returnsite peut soudainement avoir des réductions de performances massives à cet endroit sans aucun diagnostic émis. Dans la plupart des cas d'échec NRVO, C ++ 11 se termine par un move, tandis que C ++ 03 se termine par une copie.
Renvoyer un argument de fonction
L'élision est également impossible ici:
std::set<int> func(std::set<int> in){
return in;
}
en C ++ 11 c'est pas cher: en C ++ 03 il n'y a aucun moyen d'éviter la copie. Les arguments des fonctions ne peuvent pas être élidés avec la valeur de retour, car la durée de vie et l'emplacement du paramètre et de la valeur de retour sont gérés par le code appelant.
Cependant, C ++ 11 peut passer de l'un à l'autre. (Dans un exemple moins jouet, quelque chose pourrait être fait pour le set).
push_back ou insert
Enfin, l'élision dans les conteneurs ne se produit pas, mais les surcharges C ++ 11 rvalue déplacent les opérateurs d'insertion, ce qui économise des copies.
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) );
en C ++ 03 un temporaire whateverest créé, puis il est copié dans le vecteur v. 2 std::stringtampons sont alloués, chacun avec des données identiques, et un est rejeté.
En C ++ 11, un temporaire whateverest créé. La whatever&& push_backsurcharge est alors movetemporaire dans le vecteur v. Un std::stringtampon est alloué et déplacé dans le vecteur. Un vide std::stringest jeté.
Affectation
Volé de la réponse de @ Jarod42 ci-dessous.
L'élision ne peut pas se produire avec l'affectation, mais le déplacement peut.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
some_functionrenvoie ici un candidat à élider, mais comme il n'est pas utilisé pour construire directement un objet, il ne peut pas être élidé. En C ++ 03, les résultats ci-dessus entraînent la copie du contenu du temporaire some_value. En C ++ 11, il est déplacé vers some_value, qui est fondamentalement gratuit.
Pour le plein effet de ce qui précède, vous avez besoin d'un compilateur qui synthétise les constructeurs de mouvements et les affectations pour vous.
MSVC 2013 implémente les constructeurs de déplacement dans des stdconteneurs, mais ne synthétise pas les constructeurs de déplacement sur vos types.
Les types contenant std::vectors et similaires n'obtiennent donc pas de telles améliorations dans MSVC2013, mais commenceront à les obtenir dans MSVC2015.
clang et gcc ont depuis longtemps implémenté des constructeurs de mouvements implicites. Le compilateur d'Intel 2013 prendra en charge la génération implicite de constructeurs de déplacement si vous réussissez -Qoption,cpp,--gen_move_operations(ils ne le font pas par défaut dans un effort de compatibilité croisée avec MSVC2013).