web-dev-qa-db-fra.com

Y a-t-il des situations où l'auto-affectation est utile?

Il est généralement connu que lors de la mise en œuvre d'un opérateur d'affectation, il faut se protéger contre l'auto-affectation, au moins lorsque la classe a des membres non-POD. Habituellement, c'est (ou est équivalent à):

Foo& operator=(const Foo& other)
{
  if (&other == this)
     return *this;
  ... // Do copy
}

Pour quelles raisons ne pas insérer automatiquement la protection d’auto-affectation? Existe-t-il des cas d'utilisation où l'auto-affectation fait quelque chose de non trivial et pratique?

Foo& operator=(const Foo& other)
{
  if (&other == this)
  {
    // Do something non-trivial
  }
  else
  {
    // Do copy
  }
  return *this;
}

Pour résumer les réponses et la discussion maintenant

On dirait que l'auto-affectation non triviale ne peut jamais être vraiment utile. La seule option proposée était d'y mettre un assert afin de détecter certaines erreurs logiques. Mais il existe des cas d'auto-affectation tout à fait légitimes comme a = std::min(a, b), donc même cette option est très douteuse.

Mais il existe deux implémentations possibles d'une auto-affectation triviale:

  1. Ne faites rien si &other == this. Toujours fonctionner, mais peut avoir un impact négatif sur les performances en raison d'une branche supplémentaire. Mais dans un opérateur d'affectation défini par l'utilisateur, le test doit être presque toujours explicitement effectué.
  2. Copiez chaque membre sur lui-même. C'est ce qui se fait par défaut. Si les membres utilisent également des opérateurs d'affectation par défaut, cela peut être plus rapide, car ne nécessite pas de branchement supplémentaire.

Je ne vois toujours pas pourquoi la norme C++ ne pouvait pas garantir cela dans un opérateur d'affectation défini par l'utilisateur &other != this. Si vous ne voulez pas de branchement, utilisez l'opérateur par défaut. Si vous redéfinissez l'opérateur, un test est de toute façon nécessaire ...

24
aparpara

La protection d'auto-affectation n'est nécessaire que pour les types où le code ignoré est dangereux lorsqu'il est appliqué à lui-même. Considérez le cas où vous avez un opérateur d'affectation fourni par l'utilisateur car chaque objet individuel a une sorte d'identifiant, que vous ne voulez pas copier. Eh bien, vous pouvez très bien "copier" les autres valeurs dans les cas d'auto-affectation. Ainsi, l'insertion d'un test d'auto-affectation invisible ne fait qu'ajouter une branche conditionnelle inutile et potentiellement coûteuse.

Il ne s'agit donc pas de l'auto-affectation utile; il s'agit d'une auto-affectation qui n'a pas toujours besoin de protection.

De plus, C++ n'aime généralement pas ajouter du code comme ça à votre code sans que vous le demandiez explicitement. Cela se fait généralement en termes de fonctions entières, ne fait pas partie des fonctions. Même les appels de destructeurs à la fin des blocs sont quelque chose que vous avez demandé lorsque vous mettez l'objet à détruire sur la pile.

19
Nicol Bolas

Il existe des algorithmes où cela peut se produire.

  1. Vous savez que les lhs et les rhs peuvent être les mêmes, mais il est plus simple de faire l'affectation que de vérifier. Par exemple, considérez a = std::min(a,b); - plus simple et peut-être plus facile à comprendre que if (a > b) a = b; - considérez maintenant des exemples plus compliqués de choses similaires.

  2. Vous ne savez pas si lhs et rhs peuvent être identiques, car ils peuvent avoir été transmis ailleurs.

Ces algorithmes où cela peut arriver ne sont pas rares.

18
davidbak