std::swap()
est utilisé par de nombreux conteneurs std (tels que std::list
et std::vector
) pendant le tri et même l'affectation.
Mais l'implémentation std de swap()
est très généralisée et plutôt inefficace pour les types personnalisés.
Ainsi, l'efficacité peut être gagnée en surchargeant std::swap()
avec une implémentation spécifique de type personnalisé. Mais comment pouvez-vous l'implémenter pour qu'il soit utilisé par les conteneurs std?
Réponses:
La bonne façon de surcharger le swap est de l'écrire dans le même espace de noms que celui que vous échangez, afin qu'il puisse être trouvé via la recherche dépendante de l'argument (ADL) . Une chose particulièrement facile à faire est:
la source
std::sort
qui utilise ADL pour permuter des éléments est C ++ 03 non conforme mais conforme C ++ 11. Aussi, pourquoi -1 une réponse basée sur le fait que les clients peuvent utiliser du code non idiomatique?Attention Mozza314
Voici une simulation des effets d'un
std::algorithm
appel génériquestd::swap
, et demandant à l'utilisateur de fournir son échange dans l'espace de noms std. Comme il s'agit d'une expérience, cette simulation utilise à lanamespace exp
place denamespace std
.Pour moi, ceci s'imprime:
Si votre compilateur imprime quelque chose de différent, il n'implémente pas correctement la "recherche en deux phases" pour les modèles.
Si votre compilateur est conforme (à l'un des C ++ 98/03/11), alors il donnera la même sortie que je montre. Et dans ce cas, exactement ce que vous craignez se produira. Et mettre votre
swap
dans namespacestd
(exp
) ne l'a pas empêché de se produire.Dave et moi sommes tous deux membres du comité et travaillons dans ce domaine de la norme depuis une décennie (et pas toujours en accord l'un avec l'autre). Mais cette question est réglée depuis longtemps et nous sommes tous les deux d'accord sur la façon dont elle a été réglée. Ne tenez pas compte de l'opinion / de la réponse d'expert de Dave dans ce domaine à vos risques et périls.
Ce problème est apparu après la publication de C ++ 98. À partir de 2001, Dave et moi avons commencé à travailler dans ce domaine . Et voici la solution moderne:
La sortie est:
Mettre à jour
Une observation a été faite que:
travaux! Alors pourquoi ne pas l'utiliser?
Considérez le cas où vous
A
êtes un modèle de classe:Maintenant, cela ne fonctionne plus. :-(
Vous pouvez donc mettre
swap
dans l'espace de noms std et le faire fonctionner. Mais vous devez vous rappeler de mettreswap
dansA
l'espace de nom » pour le cas où vous avez un modèle:A<T>
. Et puisque les deux cas fonctionneront si vous mettezswap
dansA
l'espace de noms », il est tout simplement plus facile à retenir (et à d' autres enseignent) à faire juste que d' une façon.la source
template <>
votre premier exemple, j'obtiens la sortieexp::swap(A, A)
de gcc. Alors, pourquoi ne pas préférer la spécialisation?using std::swap
ait une exception à la règle "ne jamais mettre en utilisant les instructions dans les fichiers d'en-tête"? En fait, pourquoi ne pas mettre à l'using std::swap
intérieur<algorithm>
? Je suppose que cela pourrait briser une infime minorité du code des gens. Peut-être abandonner le support et éventuellement le mettre en place?using std::swap
portée de la fonction dans vos en-têtes. Oui,swap
c'est presque un mot-clé. Mais non, ce n'est pas tout à fait un mot-clé. Il vaut donc mieux ne pas l'exporter vers tous les espaces de noms tant que vous n'êtes pas vraiment obligé de le faire.swap
est un peu commeoperator==
. La plus grande différence est que personne ne pense même à appeleroperator==
avec une syntaxe d'espace de noms qualifié (ce serait tout simplement trop moche).Vous n'êtes pas autorisé (par le standard C ++) à surcharger std :: swap, mais vous êtes spécifiquement autorisé à ajouter des spécialisations de modèle pour vos propres types à l'espace de noms std. Par exemple
alors les utilisations dans les conteneurs std (et partout ailleurs) choisiront votre spécialisation au lieu de la généraliste.
Notez également que fournir une implémentation de classe de base de swap n'est pas suffisant pour vos types dérivés. Par exemple, si vous avez
cela fonctionnera pour les classes de base, mais si vous essayez d'échanger deux objets dérivés, il utilisera la version générique de std car le swap basé sur un modèle est une correspondance exacte (et cela évite le problème de ne permuter que les parties `` de base '' de vos objets dérivés ).
REMARQUE: j'ai mis à jour ceci pour supprimer les mauvais bits de ma dernière réponse. Oh! (merci puetzk et j_random_hacker pour l'avoir signalé)
la source
Bien qu'il soit correct de ne pas généralement ajouter de choses à l'espace de noms std ::, l'ajout de spécialisations de modèle pour les types définis par l'utilisateur est spécifiquement autorisé. La surcharge des fonctions ne l'est pas. C'est une différence subtile :-)
Une spécialisation de std :: swap ressemblerait à ceci:
Sans le bit template <>, ce serait une surcharge, qui n'est pas définie, plutôt qu'une spécialisation, qui est autorisée. @ L'approche suggérée par Wilka de changer l'espace de noms par défaut peut fonctionner avec le code utilisateur (en raison de la recherche Koenig préférant la version sans espace de noms) mais ce n'est pas garanti, et en fait n'est pas vraiment censée le faire (l'implémentation STL devrait utiliser pleinement -std :: swap qualifié).
Il existe un fil de discussion sur comp.lang.c ++. Animé par une longue discussion sur le sujet. Cependant, la plupart d'entre eux concernent une spécialisation partielle (ce qu'il n'y a actuellement aucun bon moyen de faire).
la source
vector
version et elle sera utilisée .::swap
surcharge que vous avez ajoutée est plus spécialisée que lastd::swap
surchargevector
, elle capture donc l'appel et aucune spécialisation de ce dernier n'est pertinente. Je ne sais pas en quoi c'est un problème pratique (mais je ne prétends pas non plus que c'est une bonne idée!).