648 votes

Les jours de passage const std::string & comme un paramètre de plus?

J'ai entendu une récente parler de Herb Sutter, qui a suggéré que les raisons pour passer std::vector et std::string par const & sont en grande partie disparu. Il a suggéré que l'écriture d'une fonction telle que la suivante est maintenant préférable:

std::string do_something ( std::string inval )
{
   std::string return_val;
   // ... do stuff ...
   return return_val;
}

Je comprends que l' return_val sera une rvalue au point de la fonction retourne et peut donc être renvoyée à l'aide de la sémantique de déplacement, qui sont très bon marché. Toutefois, inval est encore beaucoup plus grande que la taille d'une référence (ce qui est généralement mis en œuvre comme un pointeur). C'est parce qu'un std::string a plusieurs composantes dont un pointeur sur le tas et un membre de l' char[] pour les courts optimisation de la chaîne. Il me semble donc que le passage par référence est toujours une bonne idée.

Quelqu'un peut-il expliquer pourquoi l'Herbe peut-être dit cela?

421voto

Nicol Bolas Points 133791

La raison en Herbe a dit ce qu'il dit, c'est parce que des cas comme celui-ci.

Disons que j'ai de la fonction A qui appelle la fonction B, ce qui appelle la fonction C. Et A passe d'une chaîne à travers B et en C. A ne sait pas ou se soucient C; tous A connaît est - B. C'est, C est un détail d'implémentation de B.

Disons que A est défini comme suit:

void A()
{
  B("value");
}

Si B et C prendre à la chaîne par const&, alors qu'il ressemble à quelque chose comme ceci:

void B(const std::string &str)
{
  C(str);
}

void C(const std::string &str)
{
  //Do something with `str`. Does not store it.
}

Tout cela est bien beau. Que vous soyez simplement de passage de pointeurs autour de, pas de la copie, rien ne bouge, tout le monde est heureux. C prend un const& car elle permet de ne pas stocker la chaîne. Il utilise simplement.

Maintenant, je veux faire un simple changement: C des besoins de stockage de la chaîne, quelque part.

void C(const std::string &str)
{
  //Do something with `str`.
  m_str = str;
}

Bonjour, constructeur de copie et le potentiel de l'allocation de mémoire ignorer (SSO). C++11 de la sémantique de déplacement sont censés faire il possible de supprimer l'inutile copie de la construction, droit? Et A passe temporaire; il n'y a aucune raison pourquoi C devrait avoir à copier les données. Il faut juste s'enfuir avec ce qui a été donné à elle.

Sauf qu'il ne peut pas. Parce qu'il prend un const&.

Si je change C de prendre son paramètre par valeur, que des causes justes B faire la copie dans ce paramètre; je ne gagnez rien.

Donc, si j'avais passé juste str , en valeur, à travers toutes les fonctions, en s'appuyant sur std::move aléatoire les données autour de nous, nous n'aurions pas ce problème. Si quelqu'un veut la garder, ils le peuvent. S'ils ne le font pas, oh bien.

Est-il plus cher? Oui; le déplacement d'une valeur est plus cher que l'aide de références. Est-il moins cher que la copie? Pas pour les petites chaînes de l'authentification unique. Est-ce la peine de le faire?

Cela dépend de votre cas d'utilisation. Combien avez-vous de la haine des allocations de mémoire?

173voto

justin Points 72871

Les jours de passage const std::string & comme un paramètre de plus?

Pas de. Beaucoup de gens prennent ce conseil (y compris Dave Abrahams') au-delà du domaine, il s'applique à, et à le simplifier pour s'appliquer à tous std::string paramètres -- Toujours en passant std::string , en valeur, n'est pas une "meilleure pratique" pour tout arbitraire de paramètres et d'applications, car les optimisations de ces entretiens/articles portent sur l'appliquer uniquement à un ensemble restreint de cas.

Si vous êtes de retour d'une valeur, la mutation du paramètre, ou de prendre de la valeur, puis passage par valeur pourrait sauver cher copie et l'offre de syntaxe commodité.

Comme toujours, le passage par référence const permet d'économiser beaucoup de la copie lorsque vous n'avez pas besoin d'une copie.

Maintenant, à l'exemple précis:

Cependant inval est encore beaucoup plus grande que la taille d'une référence (ce qui est généralement mis en œuvre comme un pointeur). C'est parce qu'un std::string dispose de divers composants, y compris un pointeur sur le tas et un membre char[] pour faire court optimisation de la chaîne. Il me semble donc que le passage par référence est toujours une bonne idée. Quelqu'un peut-il expliquer pourquoi l'Herbe peut-être dit cela?

Si la taille de la pile est un sujet de préoccupation (et en supposant que ce n'est pas inline/optimisé) return_val + inval > return_val -- OIE, le pic de l'utilisation des piles peut être réduite par le passage par valeur ici (remarque: la simplification excessive de ABIs). Pendant ce temps, le passage par référence const pouvez désactiver les optimisations. La raison principale ici est de ne pas éviter la croissance de la pile, mais de s'assurer de l'optimisation peut être effectuée lorsqu'il est applicable.

Les jours de passage par référence const ne sont pas des sur-les règles peu plus compliquée que ce qu'ils étaient autrefois. Si la performance est importante, vous serez sage de considérer comment vous passer de ces types, sur la base des détails que vous utilisez dans vos implémentations.

67voto

BЈовић Points 28674

Cela dépend fortement de l'implémentation du compilateur.

Toutefois, cela dépend aussi de ce que vous utilisez.

Considérons les fonctions suivantes :

bool foo1( const std::string v )
{
  return v.empty();
}
bool foo2( const std::string & v )
{
  return v.empty();
}

Ces fonctions sont mises en œuvre dans une autre unité de compilation afin d'éviter l'in-lining. Alors :
1. Si vous passez un littéral de ces deux fonctions, vous ne verrez pas beaucoup de différence dans les performances. Dans les deux cas, une chaîne de caractères de l'objet doit être créé
2. Si vous passez à un autre std::string objet, foo2 surpasser foo1car foo1 va faire une copie en profondeur.

Sur mon PC, à l'aide de g++ 4.6.1, j'ai obtenu ces résultats :

  • variable par référence: 1000000000 itérations -> temps écoulé: 2.25912 sec
  • variable par valeur: 1000000000 itérations -> temps écoulé: 27.2259 sec
  • littérale par la référence: 100000000 itérations -> temps écoulé: 9.10319 sec
  • littérale par valeur: 100000000 itérations -> temps écoulé: 8.62659 sec

44voto

bames53 Points 38303

Sauf si vous avez réellement besoin d'une copie, il est encore raisonnable de prendre en const &. Par exemple:

bool isprint(std::string const &s) {
    return all_of(begin(s),end(s),(bool(*)(char))isprint);
}

Si vous modifiez ce prendre la chaîne de valeur, alors vous finirez par le déplacement ou la copie du paramètre, et il n'y a pas besoin de cela. Non seulement est-copier/déplacer susceptibles plus cher, mais il introduit également un nouveau potentiel d'échec; le copier/déplacer pourrait jeter une exception (par exemple, l'allocation lors de la copie peut échouer) alors que la prise d'une référence à une valeur existante ne peut pas.

Si vous ne besoin d'une copie puis passage et le retour par valeur est généralement (toujours?) la meilleure option. En fait, en général, je ne serais pas s'inquiéter à ce sujet en C++03, sauf si vous trouvez que les copies supplémentaires à cause d'un problème de performances. Copie élision semble assez fiable sur les compilateurs modernes. Je pense que les gens du scepticisme et de l'insistance que vous devez vérifier avec votre tableau de prise en charge du compilateur pour RVO est pour la plupart obsolètes de nos jours.


En bref, C++11 n'a pas vraiment changer quoi que ce soit à cet égard, sauf pour les gens qui n'ont pas confiance en copie élision.

18voto

Puppy Points 90818

std::string n'est pas un POD et ses brutes de taille n'est pas le plus pertinent chose jamais. Par exemple, si vous passez une chaîne de caractères qui est au-dessus de la longueur de la SSO et l'alloués sur le tas, je m'attends à ce que le constructeur de copie de copie pas le SSO de stockage.

La raison de ce qui est recommandé, c'est parce qu' inval est construit à partir de l'argument de l'expression, et donc est toujours déplacé ou copié le cas échéant, il n'y a aucune perte de performance, en supposant que vous avez besoin de la propriété de l'argument. Si vous ne le faites pas, un const de référence pourrait encore être la meilleure façon d'aller.

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