42 votes

Pourquoi y a-t-il tant de spécialisations de std :: swap?

Tout en regardant la documentation pour std::swap,, je vois beaucoup de spécialisations.
Il ressemble à tous les conteneur STL, ainsi que de nombreuses autres mst structures spécialisées de swap.
J'ai pensé avec l'aide de gabarits, nous n'aurions pas besoin de tous ces spécialisations?

Par exemple,
Si j'écris mes propres pair il fonctionne correctement avec l'basées sur des modèles de version:

template<class T1,class T2> 
struct my_pair{
    T1 t1;
    T2 t2;
};

int main() {
    my_pair<int,char> x{1,'a'};
    my_pair<int,char> y{2,'b'};
    std::swap(x,y);
} 

Donc, ce qu'il est obtenu à partir spécialisée std::pair?

template< class T1, class T2 >
void swap( pair<T1,T2>& lhs, pair<T1,T2>& rhs );

Je suis aussi de gauche demandais si je devrais écrire mes propres spécialisations de classes personnalisées,
ou tout simplement en s'appuyant sur la version du modèle.

37voto

user2079303 Points 4916

Donc, ce qu'il est obtenu à partir spécialisée std::pair?

Les performances. Le générique de swap est généralement suffisant (depuis C++11), mais rarement optimal (pour std::pair, et pour la plupart des autres structures de données).

Je suis aussi de gauche demandais si je devrais écrire mes propres spécialisations de classes personnalisées, ou tout simplement en s'appuyant sur la version du modèle.

Je suggère en s'appuyant sur le modèle par défaut, mais si le profilage se montre être un goulot d'étranglement, de savoir qu'il y a probablement de la place pour l'amélioration. L'optimisation prématurée et tout ça...

33voto

Vittorio Romeo Points 2559

std::swap est mis en œuvre le long de la lignes de code ci-dessous:

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); 
    t1 = std::move(t2);
    t2 = std::move(temp);
}

(Voir "Comment la bibliothèque standard de mettre en œuvre std::swap?" pour plus d'informations.)

Donc, ce qu'il est obtenu à partir spécialisée std::pair?

std::swap peuvent être spécialisés dans la manière suivante (simplifié de libc++):

void swap(pair& p) noexcept(is_nothrow_swappable<first_type>{} &&
                            is_nothrow_swappable<second_type>{})
{
    using std::swap;
    swap(first,  p.first);
    swap(second, p.second);
}

Comme vous pouvez le voir, swap est appelée directement sur les éléments de la paire à l'aide de l'ADL: cela permet personnalisés et potentiellement plus rapide implémentations de swap à être utilisé sur first et second (ces implémentations peuvent exploiter la connaissance de la structure interne des éléments pour plus de performance).

(Voir "Comment est - using std::swap activer ADL?" pour plus d'informations.)

11voto

Mark B Points 60200

C’est vraisemblablement pour des raisons de performances dans le cas où les types contenus de pair sont peu coûteux à échanger, mais coûteux à copier, comme vector . Puisqu'il peut appeler la permutation sur first et second au lieu de faire une copie avec des objets temporaires, il peut améliorer considérablement les performances du programme.

5voto

plugwash Points 795

La raison en est des performances, en particulier avant c++11.

Considérer quelque chose comme un "Vecteur" de type. Le Vecteur a trois champs: la taille, la capacité et un pointeur vers les données réelles. Il est constructeur de copie et l'assignation de copie copie les données réelles. Le C++11 version a également un constructeur de déplacement et d'assignation de déplacement que de voler le pointeur en paramètre le pointeur sur la source de l'objet à null.

Un Vecteur de dédié swap de mise en œuvre peut simplement échanger les champs.

Un générique de swap de mise en œuvre, selon le constructeur de copie, copie de l'assignation et le destructeur entraînera des données de la copie et de la dynamique de l'allocation/libération de mémoire.

Un générique de swap de mise en œuvre, selon le constructeur de déplacement, d'assignation de déplacement et destructeur permettra d'éviter toutes les données de la copie ou de l'allocation de la mémoire, mais elle laissera quelques redondant invalide et nul vérifie l'optimiseur peut ou peut ne pas être en mesure d'optimiser loin.


Alors pourquoi spécialisé swap de mise en œuvre de "Paire"? Pour une paire de int et char il n'est pas nécessaire. Ils sont de la plaine de vieux types de données afin générique swap est tout simplement parfait.

Mais que faire si j'ai une paire de dire et Vecteur de String ? Je veux utiliser le spécialiste des opérations de swap de ces types et j'ai donc besoin d'un swap de l'opération sur la paire type qui gère par échange de composants.

4voto

Le moyen le plus efficace pour échanger deux paires n'est pas le même que le moyen le plus efficace pour échanger deux vecteurs. Les deux types ont une mise en œuvre différente, différentes variables membres et les différentes fonctions de membre.

Il n'y a pas de juste moyen générique de "swap" de deux objets de cette manière.

Je veux dire, bien sûr, pour un copiable type que vous pourriez faire ceci:

T tmp = a;
a = b;
b = tmp;

Mais c'est horrible.

Pour un type mobile, vous pouvez ajouter quelques std::move et empêcher les copies, mais vous avez encore besoin de "swap" de la sémantique à la prochaine couche vers le bas afin d'avoir des utiles sémantique de déplacement. À un certain moment, vous avez besoin de se spécialiser.

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