104 votes

En C++, il est toujours mauvaise pratique pour retourner un vecteur d’une fonction ?

Version courte: Il est courant de retour des objets volumineux tels que les vecteurs/matrices-dans de nombreux langages de programmation. Est-ce le style de maintenant acceptable dans C++0x si la classe possède un constructeur de déplacement, ou de faire les programmeurs en C++ considérer que c'est bizarre/laid/abomination?

Version longue: Dans C++0x est-ce encore considéré comme une mauvaise forme?

std::vector<std::string> BuildLargeVector();
...
std::vector<std::string> v = BuildLargeVector();

La version traditionnelle devrait ressembler à ceci:

void BuildLargeVector(std::vector<std::string>& result);
...
std::vector<std::string> v;
BuildLargeVector(v);

Dans la version la plus récente, la valeur renvoyée par BuildLargeVector est une rvalue, donc v serait construit à l'aide du constructeur de déplacement de l' std::vector, en supposant (N)RVO n'a pas lieu.

Même avant le C++0x la première forme souvent d'être "efficace" à cause de (N)RVO. Cependant, (N)RVO est à la discrétion du compilateur. Maintenant que nous avons des références rvalue il est garanti qu'aucune copie en profondeur aura lieu.

Edit: la Question n'est pas vraiment à propos de l'optimisation. Les deux formes ont presque identiques performances dans le monde réel des programmes. Alors que, dans le passé, la première forme pourrait avoir eu l'ordre de grandeur de moins bonnes performances. En conséquence, la première forme a été l'un des principaux odeur de code dans la programmation en C++ pour un long moment. Pas plus, j'espère?

73voto

Peter Alexander Points 31990

Dave Abrahams a une analyse assez complète de la vitesse de passage/retourner des valeurs.

Courte réponse, si vous devez retourner une valeur puis retourner une valeur. N’utilisez pas les références de sortie car le compilateur le fait quand même. Bien sûr il y a des mises en garde, alors vous devriez lire cet article.

37voto

Jerry Coffin Points 237758

Au moins de l'OMI, il est généralement une mauvaise idée, mais pas pour des raisons d'efficacité. C'est une mauvaise idée parce que la fonction en question doit généralement être écrit comme un algorithme générique qui produit sa sortie via un itérateur. Presque n'importe quel code qui accepte ou renvoie d'un conteneur au lieu d'opérer sur les itérateurs doit être considérée comme suspecte.

Ne vous méprenez pas: il y a des fois il est logique de passer autour de la collection de comme des objets (par exemple, des chaînes de caractères), mais pour l'exemple cité, j'avais envisager l'adoption ou le retour de l'vecteur une mauvaise idée.

18voto

peterchen Points 21792

L'essentiel est:

Copie Élision et RVO peut éviter le "effrayant d'exemplaires" (le compilateur n'est pas nécessaire de mettre en œuvre ces optimisations, et dans certaines situations, il ne peut pas être appliquée)

C++ 0x références RValue permettre une chaîne/vecteur des implémentations qui garantit que.

Si vous pouvez abandonner les anciens compilateurs / STL implémentations, le retour des vecteurs librement (et assurez-vous que vos propres objets à l'appui, aussi). Si votre base de code de besoin à l'appui de "moindre" compilateurs, s'en tenir à l'ancien style.

Malheureusement, cela a une influence majeure sur vos interfaces. Si C++ 0x n'est pas une option, et vous avez besoin de garanties, vous pouvez utiliser à la place de référence-prise en compte ou de copie sur écriture des objets dans certains scénarios. Ils ont des inconvénients avec le multithreading.

(Je veux juste une réponse en C++ serait simple et simple et sans conditions).

6voto

stinky472 Points 4864

Je pense toujours que c'est une mauvaise pratique, mais il est intéressant de noter que mon équipe utilise MSVC 2008 et GCC 4.1, nous ne sommes donc pas en utilisant les derniers compilateurs.

Déjà beaucoup de points chauds montré dans vtune avec MSVC 2008 descendit à la chaîne de la copie. Nous avons eu un code comme ceci:

String Something::id() const
{
    return valid() ? m_id: "";
}

... notez que nous avons utilisé notre propre type de Chaîne (cela était nécessaire parce que nous offrons un kit de développement logiciel où plugin écrivains pourraient être à l'aide de différents compilateurs et donc différents, incompatibles implémentations de std::string/std::wstring).

J'ai fait un simple changement en réponse à l'appel graphique échantillonnage de la session de profilage montrant String::String(const String&) prendre jusqu'à une quantité importante de temps. Des méthodes comme dans l'exemple ci-dessus ont été les plus gros contributeurs (en fait, la session de profilage ont montré la mémoire de l'allocation et la libération de l'un des plus grands points chauds, avec la Chaîne de constructeur de copie étant le principal contributeur pour les allocations).

La modification que j'ai faite a été simple:

static String null_string;
const String& Something::id() const
{
    return valid() ? m_id: null_string;
}

Pourtant, cela fait un monde de différence! Le hotspot est allé loin dans la suite profiler sessions, et en plus de cela, nous faisons beaucoup de tests pour les tests unitaires à garder une trace de nos performances de l'application. Toutes sortes de test de performance de fois a chuté de façon significative après ces simples changements.

Conclusion: nous ne sommes pas à l'aide de l'absolu derniers compilateurs, mais nous avons encore ne semblent pas dépendre du compilateur optimisant loin de la copie pour le retour en valeur de façon fiable (du moins pas dans tous les cas). Qui peut ne pas être le cas pour ceux qui utilisent les nouveaux compilateurs comme MSVC 2010. Je suis impatient quand on peut utiliser le C++0x et simplement l'utilisation des références rvalue et ne jamais avoir à s'inquiéter que nous sommes pessimizing notre code par le retour des classes complexes en valeur.

[Modifier] Comme Nate l'a souligné, RVO s'applique à retourner temporaires créés à l'intérieur d'une fonction. Dans mon cas, il n'y avait pas de telles temporaires (sauf pour les invalides de la branche où l'on construit une chaîne de caractères vide) et donc RVO n'aurait pas été applicable.

3voto

Nemanja Trifunovic Points 17239

Juste pour pinailler un peu : il n’est pas courante dans de nombreux langages de programmation renvoient des matrices de fonctions. Dans la plupart d'entre eux, une référence au tableau est retournée. En C++, l’analogie plus proche serait de retour``

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X