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.
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.
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);
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é dansstd
, à 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);
.
De nombreux composants de la bibliothèque standard (dans
std
) appellentswap
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 deswap
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.