Combien de données sont copiées, lors du retour d'un std :: vector dans une fonction et quelle sera la taille d'une optimisation pour placer le std :: vector en magasin libre (sur le tas) et renvoyer un pointeur à la place, c'est-à-dire:
std::vector *f()
{
std::vector *result = new std::vector();
/*
Insert elements into result
*/
return result;
}
plus efficace que:
std::vector f()
{
std::vector result;
/*
Insert elements into result
*/
return result;
}
?
c++
return-value
stdvector
Morten
la source
la source
f
?Réponses:
En C ++ 11, c'est la méthode préférée:
Autrement dit, retour par valeur.
Avec C ++ 11,
std::vector
a une sémantique de déplacement, ce qui signifie que le vecteur local déclaré dans votre fonction sera déplacé en retour et dans certains cas, même le déplacement peut être élidé par le compilateur.la source
return std::move(v);
désactivera l'élision de mouvement même si c'était possible avec justereturn v;
. Donc, ce dernier est préféré.Vous devez retourner par valeur.
La norme a une particularité pour améliorer l'efficacité du retour en valeur. C'est ce qu'on appelle "élision de copie", et plus précisément dans ce cas "l'optimisation de la valeur de retour nommée (NRVO)".
Compilateurs ne pas mettre en œuvre, mais là encore compilateurs n'ont à mettre en œuvre la fonction inline (ou effectuer une optimisation du tout). Mais les performances des bibliothèques standard peuvent être assez médiocres si les compilateurs n'optimisent pas, et tous les compilateurs sérieux implémentent l'inlining et NRVO (et d'autres optimisations).
Lorsque NRVO est appliqué, il n'y aura pas de copie dans le code suivant:
Mais l'utilisateur peut vouloir faire ceci:
Copy elision n'empêche pas ici une copie car c'est une affectation plutôt qu'une initialisation. Cependant, vous devez toujours renvoyer par valeur. En C ++ 11, l'affectation est optimisée par quelque chose de différent, appelé "déplacer la sémantique". En C ++ 03, le code ci-dessus provoque une copie, et bien qu'en théorie un optimiseur puisse l'éviter, en pratique c'est trop difficile. Donc au lieu de
myvec = f()
, en C ++ 03, vous devriez écrire ceci:Il existe une autre option, qui consiste à offrir une interface plus flexible à l'utilisateur:
En plus de cela, vous pouvez également prendre en charge l'interface vectorielle existante:
Cela peut être moins efficace que votre code existant, si votre code existant utilise
reserve()
d'une manière plus complexe qu'un montant fixe à l'avance. Mais si votre code existant appelle essentiellementpush_back
le vecteur à plusieurs reprises, alors ce code basé sur un modèle devrait être aussi bon.la source
Il est temps que je poste une réponse sur RVO , moi aussi ...
Si vous retournez un objet par valeur, le compilateur optimise souvent cela pour qu'il ne soit pas construit deux fois, car il est superflu de le construire dans la fonction en tant que temporaire, puis de le copier. C'est ce qu'on appelle l'optimisation de la valeur de retour: l'objet créé sera déplacé au lieu d'être copié.
la source
Un idiome pré-C ++ 11 courant consiste à transmettre une référence à l'objet en cours de remplissage.
Ensuite, il n'y a pas de copie du vecteur.
la source
Si le compilateur prend en charge l'optimisation de la valeur de retour nommée ( http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx ), vous pouvez directement renvoyer le vecteur à condition qu'il n'y ait pas:
NRVO optimise les appels de constructeur et de destructeur de copie redondants et améliore ainsi les performances globales.
Il ne devrait y avoir aucune différence réelle dans votre exemple.
la source
Et si vous voulez l'imprimer sur main (), vous devez le faire en boucle.
la source
Aussi agréable que puisse être le "retour par valeur", c'est le genre de code qui peut conduire à une erreur. Considérez le programme suivant:
Le programme erroné ci-dessus n'indiquera aucune erreur même si l'on utilise les options de rapport GNU g ++ -Wall -Wextra -Weffc ++
Si vous devez produire une valeur, alors ce qui suit fonctionnerait au lieu d'appeler deux fois vecFunc ():
Ce qui précède ne produit pas non plus d'objets anonymes pendant l'itération de la boucle, mais nécessite une opération de copie possible (qui, comme certains le notent, peut être optimisée dans certaines circonstances. Mais la méthode de référence garantit qu'aucune copie ne sera produite. Croire que le compilateur sera perform RVO n'est pas un substitut pour essayer de construire le code le plus efficace possible. Si vous pouvez évoquer la nécessité pour le compilateur de faire RVO, vous êtes en avance sur le jeu.
la source
la source