web-dev-qa-db-fra.com

comment fournir une fonction de swap pour ma classe?

Quelle est la bonne façon d'activer mon swap dans les algorithmes STL?

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

2) Autonome swap dans le même espace de noms.

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

4) Tout ce qui précède.

Je vous remercie.

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

80
pic11

1) est l'utilisation appropriée de swap. Écrivez-le de cette façon lorsque vous écrivez du code "bibliothèque" et que vous souhaitez activer l'ADL (recherche dépendante de l'argument) sur 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 ...
}

2) Est la bonne façon de fournir une fonction swap pour votre classe.

namespace Foo{

class Bar{}; // dummy

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

}

Si swap est maintenant utilisé comme indiqué en 1), votre fonction sera trouvée. En outre, vous pouvez faire de cette fonction un ami si vous en avez absolument besoin, ou fournir un membre swap appelé 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);
}

3) Vous voulez dire une spécialisation explicite. Partiel est encore quelque chose d'autre et n'est pas possible pour les fonctions, seulement les structures/classes. En tant que tel, puisque vous ne pouvez pas vous spécialiser std::swap pour les classes de modèles, vous avez pour fournir une fonction gratuite dans votre espace de noms. Pas une mauvaise chose, si je puis dire. Maintenant, une spécialisation explicite est également possible, mais généralement vous ne voulez 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){
  // ...
}

}

4) Non, car 1) est distinct de 2) et 3). En outre, avoir les deux 2) et 3) conduira à toujours avoir 2) choisi, car il correspond mieux.

90
Xeo

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 pouvoir créer une fonction pour implémenter les 3 swaps (pas que cet exemple de classe le justifie), vous faites exactement comme Xeo l'a dit dans (2) ... sans spécialisation mais faites simplement une fonction modèle standard:

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 cet échange même si vous ne faites pas référence à cet espace de noms à l'aide d'ADL:

using std::swap;
swap(a,b);
1
Ben

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

Il est un comportement indéfini d'ajouter des déclarations ou des définitions à l'espace de noms std ou à tout espace de noms imbriqué dans std, à quelques exceptions près indiquées ci-dessous

Et swap n'est pas désigné comme l'une de ces exceptions. Donc, ajouter votre propre surcharge swap à l'espace de noms std est un comportement non défini.

On dit également que la bibliothèque standard utilise un appel non qualifié à la fonction swap afin d'appeler swap défini par l'utilisateur pour une classe d'utilisateurs si un tel swap défini par l'utilisateur est fourni .

échangeable (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 swap, elle utilise l'équivalent de using std::swap; swap(t, u);.

échange (www.cplusplus.com) :

De nombreux composants de la bibliothèque standard (dans std) appellent swap d'une manière non qualifiée pour autoriser des surcharges personnalisées pour les non -types fondamentaux à appeler à la place de cette version générique: les surcharges personnalisées de swap déclarées dans le même espace de noms que le type pour lequel elles sont fournies sont sélectionnées via la recherche dépendante de l'argument sur cette version générique.

Mais notez que l'utilisation directe du std::swap la fonction d'une classe définie par l'utilisateur appelle la version générique de std::swap au lieu du swap défini par l'utilisateur:

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

Il est donc recommandé d'appeler la fonction swap dans le 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.
0
anton_rh