89 votes

Quelles sont les conditions que doivent remplir les classes de clés std::map pour être des clés valides ?

Je souhaite faire correspondre des objets d'une classe donnée à des objets d'une autre classe. La classe que je veux utiliser comme clé, cependant, n'a pas été écrite par moi et est une simple struct avec quelques valeurs. std::map ordonne son contenu, et je me demandais comment il le fait, et si n'importe quelle classe arbitraire peut être utilisée comme clé ou s'il y a un ensemble d'exigences (opérateurs et autres) qui doivent être définies.

Si c'est le cas, je pourrais créer un wrapper pour la classe qui implémente les opérateurs utilisés par map. J'ai juste besoin de savoir ce que je dois implémenter en premier, et aucune des références de la classe que j'utilise n'est disponible. trouvé en ligne les spécifier.

75voto

James Kanze Points 96599

Il suffit que la clé soit copiable et cessible. L'ordre dans la carte est défini par le troisième argument du modèle (et l'argument du constructeur, s'il est utilisé). Cet ordre est défini par le troisième argument du modèle (et l'argument du constructeur, le cas échéant). Défauts à std::less<KeyType> qui, par défaut, est le < de l'opérateur, mais il n'est pas nécessaire d'utiliser les valeurs par défaut. Il suffit d'écrire une comparaison (de préférence sous la forme d'un objet fonctionnel) :

struct CmpMyType
{
    bool operator()( MyType const& lhs, MyType const& rhs ) const
    {
        //  ...
    }
};

Notez qu'il doit définir un ordre strict, c'est-à-dire que si CmpMyType()( a, b ) retourne vrai, alors CmpMyType()( b, a ) doit renvoyer false, et si les deux renvoient false, les éléments sont considérés comme égaux (membres de la même classe d'équivalence). même classe d'équivalence).

0 votes

+1 En effet, ce sont la copiabilité et la cessibilité qui sont les véritables exigences.

0 votes

Pourquoi "de préférence en tant qu'objet fonctionnel". Pourquoi pas en tant qu'opérateur < sur la classe candidate ? Certes, le PO ne contrôle pas la classe et ne peut donc pas ajouter d'opérateur, mais dans mon cas, c'est possible. Est-il indésirable d'ajouter un opérateur de comparaison, et si oui, pourquoi ?

4 votes

@nurdglaw J'ai assisté récemment à une conférence au cours de laquelle l'un des gourous a insisté sur le fait qu'il fallait fournir un service d'information à l'ensemble de la population. operator< pour chaque classe. Il a donné l'exemple des chaises. On peut les classer par hauteur, par nombre de pieds ou même par couleur, mais ce choix est totalement arbitraire et en fait il n'y a pas d'ordre naturel pour les chaises, mais c'est plutôt le contenant qui doit choisir de quelle manière les chaises doivent être classées. Trop souvent, un operator< qui semble être un choix évident n'est en fait qu'une possibilité parmi d'autres et n'a donc pas sa place dans la classe....son raisonnement allait dans ce sens.

26voto

BЈовић Points 28674

Vous devez définir l'opérateur<, par exemple comme ceci :

struct A
{
  int a;
  std::string b;
};

// Simple but wrong as it does not provide the strict weak ordering.    
// As A(5,"a") and A(5,"b") would be considered equal using this function.
bool operator<(const A& l, const A& r )
{
  return ( l.a < r.a ) && ( l.b < r.b );
}

// Better brute force.
bool operator<(const A& l, const A& r )
{ 
    if ( l.a < r.a )  return true;
    if ( l.a > r.a )  return false;

    // a are equal, compare b
    return ( l.b < r.b );
}

// This can often be seen written as
bool operator<(const A& l, const A& r )
{
   // This is fine for a small number of members.
   // But I prefer the brute force approach when you start to get lots of members.
   return ( l.a < r.a ) ||
          (( l.a == r.a) && ( l.b < r.b ));
}

2 votes

C'est un opérateur de comparaison terrible.

1 votes

Ce n'était pas seulement terrible, c'était faux. Elle ne fournissait pas le "strict ordre faible" requis par le conteneur de la carte. J'ai fourni une version de force brute ci-dessus pour compenser. Comparez la différence entre A(5, "A") et A(5, "B").

0 votes

C'est vrai, je voulais poster un exemple simple et j'ai dû me planter. Merci Martin d'avoir corrigé l'exemple.

4voto

Nemo Points 32838

La réponse se trouve en fait dans la référence que vous citez, sous la description de l'argument du modèle "Compare".

La seule condition est que Compare (dont la valeur par défaut est less<Key> qui, par défaut, utilise operator< pour comparer des clés) doit être un "ordre faible strict".

2voto

Kerrek SB Points 194696

Même chose que pour set : La classe doit avoir un ordre strict dans l'esprit de "moins que". Soit surcharger un operator< ou fournir un prédicat personnalisé. Deux objets quelconques a y b pour lequel !(a<b) && !(b>a) seront considérées comme égales.

Le conteneur de la carte conservera tous les éléments dans l'ordre prévu par cet ordre, ce qui permet d'obtenir un temps de consultation et d'insertion par valeur de clé de O(log n).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X