Le tri d'une telle vector
ou de toute autre plage applicable (itérateur d'entrée mutable) d'objets personnalisés de type X
peut être réalisé à l'aide de diverses méthodes, notamment en utilisant des algorithmes de bibliothèque standard comme
Comme la plupart des techniques, pour obtenir un ordre relatif des X
éléments, ont déjà été postées, je commencerai par quelques notes sur "pourquoi" et "quand" pour utiliser les différentes approches.
La "meilleure" approche dépendra de différents facteurs:
- Le tri des plages d'
X
objets est-il une tâche courante ou rare (ces plages seront-elles triées à plusieurs endroits différents dans le programme ou par les utilisateurs de la bibliothèque)?
- Le tri requis est-il «naturel» (attendu) ou existe-t-il plusieurs façons de comparer le type à lui-même?
- Les performances posent-elles un problème ou le tri des plages d'
X
objets doit-il être infaillible?
Si le tri des plages de X
est une tâche courante et que le tri obtenu est à prévoir (c'est- X
à- dire qu'il n'enveloppe qu'une seule valeur fondamentale), alors irait probablement en surcharge operator<
car il permet un tri sans fuzz (comme passer correctement les comparateurs appropriés) et donne à plusieurs reprises les résultats attendus résultats.
Si le tri est une tâche courante ou susceptible d'être requise dans différents contextes, mais qu'il existe plusieurs critères qui peuvent être utilisés pour trier les X
objets, je choisirais des foncteurs ( operator()
fonctions surchargées de classes personnalisées) ou des pointeurs de fonction (c'est-à-dire un foncteur / fonction pour l'ordre lexical et un autre pour l'ordre naturel).
Si le tri des plages de type X
est rare ou peu probable dans d'autres contextes, j'ai tendance à utiliser des lambdas au lieu d'encombrer tout espace de noms avec plus de fonctions ou de types.
Cela est particulièrement vrai si le tri n'est pas "clair" ou "naturel" d'une manière ou d'une autre. Vous pouvez facilement obtenir la logique derrière la commande lorsque vous regardez une lambda qui est appliquée en place alors qu'elle operator<
est opague à première vue et vous devez rechercher la définition pour savoir quelle logique de commande sera appliquée.
Notez cependant qu'une seule operator<
définition est un point de défaillance unique alors que plusieurs lambas sont de multiples points de défaillance et nécessitent une plus grande prudence.
Si la définition de operator<
n'est pas disponible là où le tri est effectué / le modèle de tri est compilé, le compilateur peut être contraint d'effectuer un appel de fonction lors de la comparaison d'objets, au lieu d'inclure la logique de classement qui pourrait être un grave inconvénient (au moins lorsque optimisation du temps de liaison / génération de code n'est pas appliquée).
Façons d'obtenir la comparabilité de class X
afin d'utiliser des algorithmes de tri de bibliothèque standard
Soit std::vector<X> vec_X;
etstd::vector<Y> vec_Y;
1. Surcharger T::operator<(T)
ou operator<(T, T)
utiliser des modèles de bibliothèque standard qui n'attendent pas de fonction de comparaison.
Soit membre de surcharge operator<
:
struct X {
int i{};
bool operator<(X const &r) const { return i < r.i; }
};
// ...
std::sort(vec_X.begin(), vec_X.end());
ou gratuit operator<
:
struct Y {
int j{};
};
bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
// ...
std::sort(vec_Y.begin(), vec_Y.end());
2. Utilisez un pointeur de fonction avec une fonction de comparaison personnalisée comme paramètre de fonction de tri.
struct X {
int i{};
};
bool X_less(X const &l, X const &r) { return l.i < r.i; }
// ...
std::sort(vec_X.begin(), vec_X.end(), &X_less);
3. Créez une bool operator()(T, T)
surcharge pour un type personnalisé qui peut être passé comme foncteur de comparaison.
struct X {
int i{};
int j{};
};
struct less_X_i
{
bool operator()(X const &l, X const &r) const { return l.i < r.i; }
};
struct less_X_j
{
bool operator()(X const &l, X const &r) const { return l.j < r.j; }
};
// sort by i
std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
// or sort by j
std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
Ces définitions d'objets de fonction peuvent être écrites un peu plus génériques à l'aide de C ++ 11 et de modèles:
struct less_i
{
template<class T, class U>
bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; }
};
qui peut être utilisé pour trier n'importe quel type avec le i
support des membres <
.
4. Passez une fermeture anonyme (lambda) comme paramètre de comparaison aux fonctions de tri.
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
Où C ++ 14 permet une expression lambda encore plus générique:
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
qui pourrait être enveloppé dans une macro
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
rendre la création d'un comparateur ordinaire assez fluide:
// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));