1
votes

Pouvez-vous transformer std :: map en une carte non ordonnée avec un comparateur personnalisé?

En raison de l'utilisation d'une bibliothèque dont je ne souhaite pas modifier le code, je me trouve obligé d'utiliser std :: map .

struct compareIdentifiers
{
    bool operator()(const Identifier& a, const Identifier& b) const
    {
        // return a < b;
        return true;
    }
};

typedef std::map<Identifier, String, compareIdentifiers> IdentifierMap;


5 commentaires

Pourquoi ne pas utiliser std :: unordered_map si c'est ce que vous voulez? Ou copier sur une carte non ordonnée? Quel est le problème réel que vous devez résoudre? Pourquoi avez-vous besoin de traiter le std :: map ordonné comme non ordonné?


En ce qui concerne le message d'erreur, vous devez spécialiser std :: hash pour votre type de clé si vous souhaitez utiliser std :: unordered_map . Il existe de nombreux tutoriels et exemples partout sur Internet si vous cherchez un peu.


Si vous devez renvoyer une valeur constante, cette constante ne peut pas être true , car elle n'est pas valide pour que comp (x, x) soit vrai pour n'importe quel objet. Vous pouvez toujours renvoyer false , ce qui signifie que la map peut contenir au plus une paire clé-valeur. Ce n'est probablement pas la manière de faire ce que vous voulez faire.


Pourquoi ne pas implémenter simplement une fonction de comparaison simple? Je ne sais pas ce qu'est Identifier , mais je parie qu'il a une représentation numérique ou chaîne qui mériterait d'appliquer un opérateur <.


J'ai finalement eu une percée mentale. Ma solution est IdentifierA.getCharPointer (). GetAddress () La raison pour laquelle je n'ai pas fait cela avant est parce que je pensais que la seule façon de comparer les classes Indentifier était avec une méthode de comparaison de chaînes lente.


4 Réponses :


-4
votes

Appelez les paramètres de vos opérateurs binaires lho et tho (opérande de gauche et opérande de droite), cela rendra immédiatement clair ce que vous êtes censé retourner. (Vrai dans le cas où lho https://en.cppreference.com/w/cpp/named_req/Compare

Il est si facile d'en casser un, je viens d'apprendre à la dure.

m2c


6 commentaires

Qu'est-ce que cela a à voir avec la question réelle ?


Il demandait quoi revenir de la comparaison. En lisant une autre réponse mieux que la mienne, je me rends compte maintenant qu'il demandait une valeur sûre à renvoyer sans faire de comparaison. Je suis content d'avoir écrit le conseil de suivre les exigences de comparaison car, comme l'ont dit d'autres personnes, retourner constamment vrai ou faux a des effets secondaires assez importants.


S'il n'y a pas de comparaison faite de toute façon, on pourrait même omettre complètement les noms pour indiquer que les paramètres ne sont en fait pas du tout utilisés (certains recommandent alors d'ajouter des noms dans les commentaires). Que vos noms proposés soient meilleurs ou non (je ne qualifierai pas, en fait, c'est assez subjectif - le premier argument se trouve à gauche, il doit donc être à gauche, de manière analogue pour la droite, donc explicite de toute façon), cela reste totalement hors de propos à la question elle-même.


Honnêtement, je ne vois pas de grandes différences entre cette réponse et celle acceptée. Au moins dans l'esprit. Quoi qu'il en soit désolé pour les ordures que j'ai écrites dans cette page.


Différence avec la réponse acceptée? Eh bien, vous ne perdez aucun mot sur les conséquences de renvoyer constamment vrai ou faux, n'est-ce pas? Mais c'est de cela qu'il s'agit. Si l'auteur utilisait les identifiants que vous proposez, la question ne changerait pas du tout, n'est-ce pas? C'est pourquoi votre réponse est sans rapport (contre exemple: il aurait été approprié que l'auteur ait mélangé les variables dans son code à cause de noms mal sélectionnés).


"Des ordures"? Vous jugez beaucoup trop strict. En effet, il est important de choisir de bons identifiants qui s’expliquent d'eux-mêmes, uniquement que cette question n'est pas le bon endroit pour le sujet - du moins si elle est isolée comme dans votre réponse. Mais ne vous découragez pas - la prochaine réponse correspondra certainement mieux ...



7
votes

Toujours renvoyer true n'est pas valide. Cela signifierait (par exemple) que A et B seraient tous les deux vrais. Cela contredit les exigences d'un comparateur std :: map , à savoir qu'il impose un ordre faible strict . Il est tout à fait possible que le retour de true fasse planter votre programme.

Toujours renvoyer false est valide, cela signifie effectivement que toutes les clés sont considérées comme égales. Ainsi, une seule clé pourrait être ajoutée à la carte (merci aschepler pour la correction).

Qu'est-ce qui vous empêche d'écrire un comparateur sensé?


10 commentaires

La classe d'identifiant n'a pas d'opérateur <


Pourquoi c'est un problème? Il suffit de créer quelque chose qui satisfait un ordre strict et de le mettre dans votre comparateur. L'ordre que vous utilisez peut être complètement arbitraire,


Pourquoi ne pas simplement retourner faux? Edit: C'est un problème car c'est cher de comparer la classe ... à première vue.


@ElanHickler Juste un opérateur booléen autonome <(Identifier const & x, Identifier const & y) {return / * comparaison si nécessaire * /; } et vous en avez un ...


alors pourquoi avons-nous 5 votes positifs pour "Toujours retourner faux est valide"? Cette réponse doit être supprimée ou rejetée.


@ElanHickler Rappelez-moi de ne pas essayer de vous aider à nouveau. Mis à part le ton ingrat, je peux voir que vous n'avez pas bien lu ma réponse (ou que vous ne l'avez pas comprise). Pour être clair, renvoyer false ne rompt aucune règle de C ++ (contrairement au retour true). En ce sens, c'est valable. Ce n'est cependant pas utile.


Je retire mon commentaire ... je n'ai pas réfléchi.


@john whaaat? Je l'ai lu parfaitement clair. Ted a dit que votre réponse était fausse, pas moi. Je ne sais pas mieux, c'est pourquoi je pose ici des questions?!?!?


@ElanHickler utilisant le return true comparer avec std :: map est un comportement non défini. l'utilisation du return false compare n'est pas un comportement indéfini, mais signifie que la carte ne peut contenir au plus qu'un élément, car chaque élément potentiel se compare de la même manière que tous les autres éléments potentiels.


Merci d'avoir expliqué Caleth, maintenant je comprends. Ohhhhhhhhhhh !!!!!!!!!!!!!!!! J'ai juste eu une autre idée. Au lieu d'utiliser un unordered_map, je peux faire un comparateur de hachage comme this_hash si je veux revenir à l'utilisation d'une carte. p.s. L'identifiant est déjà essentiellement un hachage. Je pensais auparavant que je devais comparer ces classes en utilisant une méthode de comparaison de chaînes plus lente.



1
votes

J'ai pu créer un hachage personnalisé pour cette classe en examinant son implémentation et ses types de retour et les types de retour de ses types de retour jusqu'à ce que je trouve une série efficace de méthodes qui retourne un type standard que je pourrais renvoyer que std :: hash a une spécialisation spécifique au type. La liste se trouve ici: http://www.cplusplus.com/reference/functional/hash /

struct IdentifierComparator
{
    bool operator()(const Identifier& left, const Identifier& right) const
    {
        return left.getCharPointer().getAddress() < right.getCharPointer().getAddress();
    }
};

typedef std::map<Identifier, String, IdentifierComparator> IdentifierMap;

Si je veux utiliser std :: map alors je peux créer un comparateur comme celui-ci:

struct IdentifierHash {
    size_t operator()(const juce::Identifier& v) const
    {
        std::hash<char*> hash;
        return hash(v.getCharPointer().getAddress());
    }
};

typedef std::unordered_map<Identifier, String, IdentifierHash> IdentifierMap;


0 commentaires

4
votes

J'ai essayé d'utiliser std :: unordered_map mais j'ai obtenu une erreur:

Erreur C2280 'std :: hash <_kty> :: hash (void)': tentative de référence à une fonction supprimée

Cela est dû au fait que la carte n'est pas ordonnée utilisant une instance de std :: hash , mais sa spécialisation pour votre classe Identifier a un opérateur supprimé () . Vous avez maintenant deux options:

  1. spécialisez std :: hash pour votre type:

    namespace std
    {
    template<>
    class hash
    {
    public:
    std::size_t operator()(Identifier const&) const
    {
        return /* whatever is appropriate */;
        // best is if you can base the hash code on already defined
        // hashes of its members
    }
    };
    }
  2. écrivez votre propre classe de hachage (en fournissant à nouveau operator () ) et fournissez-la comme troisième paramètre de modèle à votre carte non ordonnée: std :: unordered_map


0 commentaires