comment fournir une fonction d'échange pour ma classe?

87

Quelle est la bonne façon d'activer mes swapalgorithmes dans STL?

1) Membre swap. Est-ce que l' std::swaputilisation astuce SFINAE d'utiliser le membre swap.

2) Autonome swapdans le même espace de noms.

3) Spécialisation partielle de std::swap.

4) Tout ce qui précède.

Merci.

EDIT: On dirait que je n'ai pas formulé ma question clairement. Fondamentalement, j'ai une classe de modèle et j'ai besoin d'algos STL pour utiliser la méthode d'échange (efficace) que j'ai écrite pour cette classe.

pic11
la source

Réponses:

94
  1. est la bonne utilisation de swap. Écrivez-le de cette façon lorsque vous écrivez du code «bibliothèque» et que vous souhaitez activer ADL (recherche dépendante des arguments) swap. De plus, cela n'a rien à voir avec SFINAE.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs) {
    using std::swap; // enable 'std::swap' to be found
                    // if no other 'swap' is found through ADL
    // some code ...
    swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                    // or falls back on 'std::swap'
    // more code ...
}
  1. Est la bonne façon de fournir une swapfonction à votre classe.
namespace Foo {

class Bar{}; // dummy

void swap(Bar& lhs, Bar& rhs) {
    // ...
}

}

Si swapest maintenant utilisé comme indiqué en 1), votre fonction sera trouvée. De plus, vous pouvez faire de cette fonction un ami si vous en avez absolument besoin, ou fournir un membre swapappelé par la fonction gratuite:

// version 1
class Bar{
public:
    friend void swap(Bar& lhs, Bar& rhs) {
    // ....
    }
};

// version 2
class Bar{
public:
    void swap(Bar& other) {
    // ...
    }
};

void swap(Bar& lhs, Bar& rhs) {
    lhs.swap(rhs);
}

...
  1. Vous voulez dire une spécialisation explicite. Le partiel est encore quelque chose d'autre et également impossible pour les fonctions, uniquement les structures / classes. En tant que tel, puisque vous ne pouvez pas vous spécialiser std::swappour les classes de modèles, vous devez fournir une fonction gratuite dans votre espace de noms. Pas mal, si je puis dire. Désormais, une spécialisation explicite est également possible, mais généralement vous ne souhaitez pas spécialiser un modèle de fonction :
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. Non, car 1) est distinct de 2) et 3). De plus, avoir à la fois 2) et 3) conduira à toujours avoir 2) choisi, car il convient mieux.
Xeo
la source
8
Votre (1) et la question (1) ne s'alignent pas vraiment, sauf si j'ai mal interprété quelque chose. Still, +1
Dennis Zickefoose
1
@Xeo. Merci pour votre contribution. J'ai édité ma question. La STL utilise-t-elle le swap comme vous l'avez décrit dans le cas 1?
pic11
1
@pic: Oui, la STL utilisera le swap ADL que j'ai montré en 1), mais seulement s'il existe en tant que fonction libre, pas seulement en tant que fonction membre. Voir 2) et 3), les deux versions seront sélectionnées par des algorithmes. Je conseillerais 2), car 3) est dépassé et considéré comme une mauvaise pratique.
Xeo
2
Le commentaire dans le premier morceau de code est trompeur. using std::swap;n'active pas ADL, il permet simplement au compilateur de localiser std::swapsi ADL ne trouve pas une surcharge appropriée.
David Rodríguez - dribeas
6
Cette réponse est techniquement correcte, mais a cruellement besoin d'être modifiée pour plus de clarté. Le PO (1) n'est pas la bonne réponse comme une lecture trop rapide dans cette réponse semblerait l'indiquer à tort.
Howard Hinnant
1

Pour répondre à l'EDIT, où les classes peuvent être des classes modèles, vous n'avez pas du tout besoin de spécialisation. considérez une classe comme celle-ci:

template <class T>
struct vec3
{
    T x,y,z;
};

vous pouvez définir des classes telles que:

vec3<float> a;
vec3<double> b;
vec3<int> c;

si vous voulez être en mesure de créer une fonction pour implémenter les 3 swaps (pas que cet exemple de classe le justifie), vous faites comme Xeo dit dans (2) ... sans spécialisation mais créez simplement une fonction de modèle régulière:

template <class T>
void swap(vec3<T> &a, vec3<T> &b)
{
    using std::swap;
    swap(a.x,b.x);
    swap(a.y,b.y);
    swap(a.z,b.z);
}

La fonction de modèle d'échange doit être située dans le même espace de noms que la classe que vous essayez d'échanger. la méthode suivante trouvera et utilisera ce swap même si vous ne référencez pas cet espace de noms à l'aide d'ADL:

using std::swap;
swap(a,b);
Ben
la source
0

Il semble que (2) ( autonome swapdans le même espace de noms où la classe définie par l'utilisateur est déclarée ) soit le seul moyen autorisé de fournir swapune classe définie par l'utilisateur, car l'ajout de déclarations à l'espace de noms stdest généralement un comportement non défini. Extension de l'espace de noms std (cppreference.com) :

L'ajout de déclarations ou de définitions à l'espace de noms stdou à tout espace de noms imbriqué n'est pas défini std, à quelques exceptions près notées ci-dessous

Et swapne fait pas partie de ces exceptions. Donc, ajouter votre propre swapsurcharge à l' stdespace de noms est un comportement indéfini.

Il est également dit que la bibliothèque standard utilise un appel non qualifié à la swapfonction afin d'appeler défini swappar l'utilisateur pour une classe d'utilisateurs si une telle définition utilisateur swapest fournie.

Remplaçable (cppreference.com) :

De nombreuses fonctions de bibliothèque standard (par exemple, de nombreux algorithmes) s'attendent à ce que leurs arguments satisfassent Swappable , ce qui signifie que chaque fois que la bibliothèque standard effectue un échange, elle utilise l'équivalent de using std::swap; swap(t, u);.

swap (www.cplusplus.com) :

De nombreux composants de la bibliothèque standard (dans std) appellent swapde manière non qualifiée pour permettre aux surcharges personnalisées pour les types non fondamentaux d'être appelées au lieu de cette version générique: Les surcharges personnalisées de swapdéclarées dans le même espace de noms que le type pour lequel elles sont fournies sont sélectionnées via une recherche dépendante des arguments sur cette version générique.

Mais notez que l'utilisation directe de la std::swapfonction pour une classe définie par l'utilisateur appelle la version générique de std::swapau lieu de celle définie par l'utilisateur swap:

my::object a, b;
std::swap(a, b); // calls std::swap, not my::swap

Il est donc recommandé d'appeler la swapfonction en code utilisateur de la même manière que dans la bibliothèque standard:

my::object a, b;
using std::swap;
swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
anton_rh
la source