Comment procéder pour trier un vecteur contenant des objets personnalisés (c'est-à-dire définis par l'utilisateur).
Il est probable que le tri standard de l'algorithme STL avec un prédicat (une fonction ou un objet fonction) qui fonctionnerait sur l'un des champs (comme clé de tri) dans l'objet personnalisé devrait être utilisé.
Suis-je sur la bonne voie?
248
Réponses:
Un exemple simple utilisant
std::sort
Edit: Comme l'a souligné Kirill V. Lyadvinsky, au lieu de fournir un prédicat de tri, vous pouvez implémenter le
operator<
pourMyStruct
:En utilisant cette méthode, vous pouvez simplement trier le vecteur comme suit:
Edit2: Comme Kappa le suggère, vous pouvez également trier le vecteur dans l'ordre décroissant en surchargeant un
>
opérateur et en changeant un peu l'appel de tri:Et vous devez appeler sort comme:
la source
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
qui est propre et élégant.#include <functional>
utiliser "std :: supérieur".operator<
et utiliser soitstd::sort(vec.begin(), vec.end());
oustd::sort(vec.rbegin(), vec.rend());
selon que vous souhaitez avoir un ordre croissant ou décroissant.Dans l'intérêt de la couverture. J'ai proposé une implémentation utilisant des expressions lambda .
C ++ 11
C ++ 14
la source
>
au lieu de<
pour obtenir l'ordre décroissant.Vous pouvez utiliser functor comme troisième argument de
std::sort
, ou vous pouvez définiroperator<
dans votre classe.la source
const
à la fin de la signature de fonction?const
.const
mot-clé à la fin de la signature spécifie que laoperator()
fonction ne change pas l'instance de laXgreater
structure (qui en général pourrait avoir des variables membres), alorsconst
qu'indiquer pour les valeurs d'entrée spécifie seulement que ces valeurs d'entrée sont immuables.Le tri d'une telle
vector
ou de toute autre plage applicable (itérateur d'entrée mutable) d'objets personnalisés de typeX
peut être réalisé à l'aide de diverses méthodes, notamment en utilisant des algorithmes de bibliothèque standard commesort
,stable_sort
,partial_sort
oupartial_sort_copy
.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:
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)?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 surchargeoperator<
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 standardSoit
std::vector<X> vec_X;
etstd::vector<Y> vec_Y;
1. Surcharger
T::operator<(T)
ouoperator<(T, T)
utiliser des modèles de bibliothèque standard qui n'attendent pas de fonction de comparaison.Soit membre de surcharge
operator<
:ou gratuit
operator<
:2. Utilisez un pointeur de fonction avec une fonction de comparaison personnalisée comme paramètre de fonction de tri.
3. Créez une
bool operator()(T, T)
surcharge pour un type personnalisé qui peut être passé comme foncteur de comparaison.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:
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.
Où C ++ 14 permet une expression lambda encore plus générique:
qui pourrait être enveloppé dans une macro
rendre la création d'un comparateur ordinaire assez fluide:
la source
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
pour le comparateur mais lesconst
mots clés doivent être supprimés (car ce n'est pas une fonction membre).std::sort
ou similaire, mais que j'ai besoin d'une instance deCompare
, par exemple lors de l'instanciation d'unstd::set
?template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
qui pourrait être utilisé commeauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
.Tu es sur la bonne piste.
std::sort
utiliseraoperator<
comme fonction de comparaison par défaut. Donc, pour trier vos objets, vous devrez soit surcharger,bool operator<( const T&, const T& )
soit fournir un foncteur qui fait la comparaison, un peu comme ceci:L'avantage de l'utilisation d'un foncteur est que vous pouvez utiliser une fonction avec accès aux membres privés de la classe.
la source
operator<
un membre de classe (ou struct), car un global peut utiliser des membres protégés ou privés. Ou vous devriez en faire un ami de la structure C.J'étais curieux de savoir s'il y avait un impact mesurable sur les performances entre les différentes façons d'appeler std :: sort, j'ai donc créé ce test simple:
Ce qu'il fait, c'est qu'il crée un vecteur aléatoire, puis mesure le temps nécessaire pour le copier et le trier (et calculer une somme de contrôle pour éviter une élimination trop vigoureuse du code mort).
Je compilais avec g ++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
Voici les résultats:
Il semble que toutes les options, à l'exception du passage du pointeur de fonction, soient très similaires, et le passage d'un pointeur de fonction entraîne une pénalité de + 30%.
Il semble également que l'opérateur <version soit ~ 1% plus lent (j'ai répété le test plusieurs fois et l'effet persiste), ce qui est un peu étrange car cela suggère que le code généré est différent (je manque de compétences pour analyser - enregistrer - sortie temps).
la source
Oui,
std::sort()
avec un troisième paramètre (fonction ou objet) serait plus facile. Un exemple: http://www.cplusplus.com/reference/algorithm/sort/la source
Dans votre classe, vous pouvez surcharger l'opérateur "<".
la source
Voici le code utilisant lambdas
la source
la source
Vous pouvez utiliser une classe de comparaison définie par l'utilisateur.
la source
Pour trier un vecteur, vous pouvez utiliser l'algorithme sort () dans.
Le troisième paramètre utilisé peut être supérieur ou inférieur ou n'importe quelle fonction ou objet peut également être utilisé. Cependant, l'opérateur par défaut est <si vous laissez le troisième paramètre vide.
la source
si compare est faux, il fera "swap".
la source