web-dev-qa-db-fra.com

Un conteneur STL doit-il éviter de copier des éléments en lui-même lorsque le conteneur est copié en lui-même?

La question concerne l'auto-affectation. Par exemple, copier un vecteur en lui-même:

std::vector<std::string> vec(5, "hello");
vec = vec;

Le code ci-dessus doit-il effectuer 5 opérations d'affectation de chaînes en soi, ou ne rien faire? Je veux dire si la vérification suivante est valide:

std::vector operator=(const std::vector &rhs)
{
    if (this == &rhs)
        { return *this; }
    ...
}

Je travaille sur ma propre implémentation de std::variant classe (juste pour le plaisir) et intéressé si je devais ajouter une vérification d'auto-affectation au début de l'opérateur d'affectation, ou devrais-je simplement copier l'élément contenu en lui-même?

Je comprends qu'en général, cela n'a pas d'importance. Vous ne devez pas créer une classe qui utilise le fait de copier en elle-même. Mais je suis intéressé si la norme en dit long.

23
anton_rh

Les conditions d'affectation pré/post d'un conteneur spécifiées par la norme (en citant le dernier projet):

[tab: container.req]

r = a

Assure: r == a.

Cela permet mais ne rend pas obligatoire la vérification de l'affectation automatique.

18
eerorika

intéressé si je dois ajouter une vérification d'auto-affectation au début de l'opérateur d'affectation, ou dois-je simplement copier l'élément contenu en lui-même?

C++ Core Guidelines recommande de ne pas effectuer de vérification d'auto-affectation dans une classe d'utilisateurs si tous ses membres sont sûrs pour l'auto-affectation:

Application (Simple) Les opérateurs d'affectation ne doivent pas contenir le modèle if (this == &a) return *this; ???

C'est pour une raison d'efficacité. Il est peu probable que les auto-affectations se produisent dans la pratique. Ils sont rares et il est donc préférable d'éviter de faire une vérification d'auto-affectation à chaque opération. Une vérification d'auto-affectation rend probablement le code plus rapide dans le cas d'auto-affectation (très rare) et le rend plus lent dans tous les autres cas (plus courant).

Imaginez que vous attribuez un million d'éléments. Dans chaque opération d'affectation, une vérification d'auto-affectation est effectuée. Et c'est très probablement que cela est fait pour rien car aucune des affectations n'est en fait une auto-affectation. Et donc nous faisons un million de contrôles inutiles.

Si nous sautons de faire une vérification d'auto-affectation, rien de mauvais ne se produit, sauf que si l'auto-affectation se produit vraiment, nous faisons des auto-affectations inutiles de tous les membres (ce qui est parfois plus lent que de faire une seule vérification d'auto-affectation au début de l'affectation). opérateur). Mais si votre code effectue un million d'auto-affectations, c'est une raison de reconsidérer votre algorithme plutôt que d'effectuer une vérification d'auto-affectation dans toutes les opérations.

Cependant, la vérification de l'auto-affectation doit toujours être utilisée pour les classes qui ne sont pas sécurisées par auto-affectation par défaut. L'exemple est std::vector. Le vecteur copié doit d'abord supprimer les éléments existants. Mais si le vecteur de destination et le vecteur source sont le même objet, en supprimant les éléments du vecteur de destination, nous les supprimons également dans le vecteur source. Et il ne sera donc pas possible de les copier après suppression. C'est pourquoi libstdc ++ effectue une vérification d'auto-affectation pour std::vector (bien qu'il soit possible de implémenterstd::vector sans vérification d'auto-affectation).

Mais il ne le fait pas pour std::variant par exemple. Si vous copiez une variante en elle-même, la valeur contenue sera copiée en elle-même. Voir exemple en direct . Parce que le copier en lui-même est sûr pour l'auto-affectation (à condition que la valeur contenue soit sûre pour l'auto-affectation).

Ainsi, libstdc ++ effectue une vérification d'auto-affectation pour std::vector (pour assurer la sécurité d'auto-affectation), et non pour std::variant (pour l'efficacité).

4
anton_rh

[...] si je devais ajouter cette vérification au début de l'opérateur d'affectation [...]?

Vous devriez, indépendamment du fait que std::vector, ou d'autres conteneurs STL le font pour vous. Imaginez un utilisateur qui travaille avec votre bibliothèque et qui x = x, en dehors de la portée des conteneurs STL.

Passons maintenant aux exigences du conteneur STL - je crois que la norme ne spécifie pas si une affectation est requise pour effectuer une vérification pour être une auto-affectation (a traversé la majorité des Containers library section). Cela donne de la place pour les optimisations du compilateur et je crois qu'un compilateur décent devrait effectuer de telles vérifications.

1
Fureeish

Vérification this == &rhs est en fait un joli idiome bien conn , et aide à être sûr de ne rien casser en garantissant que lhs et rhs sont des objets différents. Donc, c'est valable et réellement encouragé.

Je ne sais pas si les conteneurs STL sont nécessaires pour faire la vérification.

0
lisyarus