Question

Comme la plupart des programmeurs, j’admire et essaie de suivre les principes de la programmation alphabétisée, mais en C ++, je me trouve régulièrement à l’utilisation de std :: pair , pour une multitude de tâches courantes. Mais std :: pair est, à mon humble avis, un ennemi vil de la programmation alphabète ...

Mon problème, c’est que lorsque je reviens au code que j’ai écrit il ya un jour ou deux, et que je vois les manipulations d’un std :: pair (généralement sous la forme d’un itérateur), je me pose des questions. ; qu'est-ce que iter- > first et iter- > second signifient ??? ".

Je suppose que d'autres personnes ont les mêmes doutes quant à leur code std :: pair . Je me demandais donc si quelqu'un avait trouvé de bonnes solutions pour retrouver l'alphabétisation lors de l'utilisation de . std :: pair ?

Était-ce utile?

La solution

Que diriez-vous de cela:

struct MyPair : public std::pair < int, std::string >
{
    const int& keyInt() { return first; }
    void keyInt( const int& keyInt ) { first = keyInt; }
    const std::string& valueString() { return second; }
    void valueString( const std::string& valueString ) { second = valueString; }
};

C'est un peu bavard, mais son utilisation dans votre code pourrait rendre les choses un peu plus faciles à lire, par exemple:

std::vector < MyPair > listPairs;

std::vector < MyPair >::iterator iterPair( listPairs.begin() );
if ( iterPair->keyInt() == 123 )
    iterPair->valueString( "hello" );

À part cela, je ne vois pas de solution miracle qui rendrait les choses beaucoup plus claires.

Autres conseils

std :: pair est un bon moyen de créer un " local " et de type essentiellement anonyme avec des colonnes essentiellement anonymes; Si vous utilisez une certaine paire sur un espace lexical tellement grand que vous devez nommer le type et les colonnes, j'utiliserais plutôt une struct à la place.

typedef std::pair<bool, int> IsPresent_Value;
typedef std::pair<double, int> Price_Quantity;

... vous obtenez le point.

Vous pouvez créer deux paires de getters (const et non) qui renverront simplement une référence en premier et en second, mais seront beaucoup plus lisibles. Par exemple:

string& GetField(pair& p) { return p.first; }
int& GetValue(pair& p) { return p.second; }

Vous permettra d'obtenir le champ et la valeur des membres d'une paire donnée sans avoir à vous rappeler quel membre détient quoi.

Si vous envisagez de l'utiliser beaucoup, vous pouvez également créer une macro qui générera ces accesseurs, en fonction des noms et des types: MAKE_PAIR_GETTERS (Champ, chaîne, Valeur, int) ou autre. En simplifiant les accesseurs, le compilateur pourra probablement les optimiser, ce qui leur évitera des frais généraux lors de l'exécution; et utiliser la macro en fera un jeu d'enfant pour créer ces accesseurs quelle que soit l'utilisation que vous faites des paires.

Vous pouvez utiliser des tuples boostés, mais ils ne modifient pas vraiment le problème sous-jacent: voulez-vous vraiment accéder à chaque partie de la paire / tuple avec un petit type intégral, ou voulez-vous code plus «alphabétisé». Voir cette question , j'ai posté il y a quelque temps.

Cependant, boost :: optional est un outil utile qui, j’ai trouvé, remplace bon nombre des cas où les paires / les nuplets sont vantés comme une réponse.

Récemment, je me suis retrouvé à utiliser boost :: tuple en remplacement de std :: pair . Vous pouvez définir des énumérateurs pour chaque membre. Le nom de chaque membre est donc évident:

typedef boost::tuple<int, int> KeyValueTuple;
enum {
  KEY
  , VALUE
};

void foo (KeyValueTuple & p) {
    p.get<KEY> () = 0;
    p.get<VALUE> () = 0;
}

void bar (int key, int value)
{
  foo (boost:tie (key, value));
}

BTW, les commentaires sont les bienvenus sur les coûts cachés liés à l’utilisation de cette approche.

EDIT: supprimer les noms de la portée globale.

Juste un petit commentaire sur l’espace de noms global. En général, j'utiliserais:

struct KeyValueTraits
{
  typedef boost::tuple<int, int> Type;
  enum {
    KEY
    , VALUE
  };
};

void foo (KeyValueTuple::Type & p) {
    p.get<KeyValueTuple::KEY> () = 0;
    p.get<KeyValueTuple::VALUE> () = 0;
}

Il semble que boost :: fusion lie l'identité et la valeur plus étroitement.

Comme Alex l’a mentionné, std :: pair est très pratique, mais si vous avez du mal à créer une structure et utilisez-la de la même manière, regardez std :: pair code, ce n'est pas si complexe.

Je n'aime pas std :: pair tel qu'il est utilisé dans std :: map non plus, les entrées de la carte devraient avoir la clé et la valeur de leurs membres.
J'ai même utilisé boost :: MIC pour éviter cela. Cependant, boost :: MIC a aussi un coût.

De plus, renvoyer std :: pair donne moins que du code lisible:

if (cntnr.insert(newEntry).second) { ... }

???

J'ai aussi découvert que std :: pair est couramment utilisé par les programmeurs paresseux qui avaient besoin de 2 valeurs mais ne pensaient pas pourquoi ces valeurs étaient nécessaires ensemble.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top