Codifica alfabetica vs. std :: pair, soluzioni?
-
22-07-2019 - |
Domanda
Come la maggior parte dei programmatori ammiro e cerco di seguire i principi della programmazione di Literate, ma in C ++ mi trovo di routine a usare std :: pair
, per compiti straordinari. Ma std :: pair
è, IMHO, un vile nemico della programmazione alfabetica ...
Il mio punto è quando torno al codice che ho scritto un giorno o due fa, e vedo manipolazioni di un std :: pair
(in genere come iteratore) Mi chiedo tra me e me ; che cosa significava iter- > first e iter- > second significa ??? " ;.
Suppongo che altri abbiano gli stessi dubbi quando guardano il loro codice std :: pair
, quindi mi chiedevo, qualcuno ha trovato delle buone soluzioni per recuperare l'alfabetizzazione quando si usa std :: coppia
?
Soluzione
Che ne dici di questo:
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; }
};
È un po 'prolisso, tuttavia l'utilizzo di questo nel codice potrebbe rendere le cose un po' più facili da leggere, ad esempio:
std::vector < MyPair > listPairs;
std::vector < MyPair >::iterator iterPair( listPairs.begin() );
if ( iterPair->keyInt() == 123 )
iterPair->valueString( "hello" );
Oltre a questo, non riesco a vedere nessun proiettile d'argento che renderà le cose molto più chiare.
Altri suggerimenti
std :: pair
è un buon modo per creare un " local " e tipo essenzialmente anonimo con colonne essenzialmente anonime; se stai usando una certa coppia su uno spazio lessicale così grande che devi nominare il tipo e le colonne, userei invece un semplice struct
.
typedef std::pair<bool, int> IsPresent_Value;
typedef std::pair<double, int> Price_Quantity;
... ottieni il punto.
Puoi creare due coppie di getter (const e non) che restituiranno semplicemente un riferimento al primo e al secondo, ma saranno molto più leggibili. Ad esempio:
string& GetField(pair& p) { return p.first; }
int& GetValue(pair& p) { return p.second; }
Ti consentirà di ottenere il campo e valutare i membri da una determinata coppia senza dover ricordare quale membro detiene cosa.
Se prevedi di usarlo molto, potresti anche creare una macro che genererà quei getter per te, dati i nomi e i tipi: MAKE_PAIR_GETTERS (Field, string, Value, int) o simili. Rendere i getter semplici consentirà probabilmente al compilatore di ottimizzarli, quindi non aggiungeranno sovraccarico in fase di esecuzione; e l'uso della macro renderà semplicissimo creare quei getter per qualsiasi uso tu faccia delle coppie.
Potresti usare boost tuple, ma non alterano realmente il problema di fondo: il tuo davvero vuole accedere a ciascuna parte della coppia / tupla con un piccolo tipo integrale o vuoi codice più "letterato". Vedi questa domanda ho postato qualche tempo fa.
Tuttavia, boost :: opzionale è uno strumento utile che ho trovato sostituisce alcuni dei casi in cui coppie / tuple vengono propagandate come risposta.
Di recente mi sono trovato a usare boost :: tuple
in sostituzione di std :: pair
. Puoi definire gli enumeratori per ogni membro ed è quindi ovvio che cosa sia ogni membro:
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));
}
A proposito, commenti positivi su se ci sono costi nascosti per l'utilizzo di questo approccio.
MODIFICA: rimuove i nomi dall'ambito globale.
Solo un breve commento sullo spazio dei nomi globale. In generale userei:
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;
}
Sembra che boost :: fusion
leghi l'identità e il valore più vicini.
Come accennato da Alex, std :: pair
è molto conveniente ma quando diventa confuso crea una struttura e usala allo stesso modo, dai un'occhiata a std :: pair
code, non è così complesso.
Non mi piace neanche std :: pair come usato in std :: map, le voci della mappa avrebbero dovuto avere la chiave e il valore dei membri.
Ho persino usato boost :: MIC per evitarlo. Tuttavia, boost :: MIC ha anche un costo.
Inoltre, restituendo una coppia std :: pair si ottiene un codice non leggibile:
if (cntnr.insert(newEntry).second) { ... }
???
Ho anche scoperto che std :: pair è comunemente usato dai programmatori pigri che avevano bisogno di 2 valori ma non pensavano perché questi valori fossero necessari insieme.