Perché l'uso delle tuple in C ++ non è più comune?


124

Perché nessuno sembra usare le tuple in C ++, la libreria Boost Tuple o la libreria standard per TR1? Ho letto molto codice C ++ e molto raramente vedo l'uso delle tuple, ma vedo spesso molti posti in cui le tuple risolvono molti problemi (di solito restituiscono più valori dalle funzioni).

Le tuple ti consentono di fare tutti i tipi di cose interessanti come questa:

tie(a,b) = make_tuple(b,a); //swap a and b

Questo è sicuramente meglio di questo:

temp=a;
a=b;
b=temp;

Ovviamente potresti sempre farlo:

swap(a,b);

Ma cosa succede se si desidera ruotare tre valori? Puoi farlo con le tuple:

tie(a,b,c) = make_tuple(b,c,a);

Le tuple rendono anche molto più semplice restituire più variabili da una funzione, che è probabilmente un caso molto più comune dello scambio di valori. L'utilizzo di riferimenti per restituire valori non è certamente molto elegante.

Ci sono grossi svantaggi per le tuple a cui non sto pensando? In caso contrario, perché vengono usati raramente? Sono più lenti? O è solo che le persone non sono abituate a loro? È una buona idea usare le tuple?


17
+1 per un trucco di scambio di tuple intelligente :)
kizzx2,

10
a = a ^ b; b = a ^ b; a = a ^ b;
Gerardo Marset,

3
Le tuple IMO sono utili nelle lingue di digitazione deboli o nelle lingue in cui sono strutture native. Ad esempio in Python o PHP rendono la vita più semplice, mentre in C ++ c'è troppa digitazione (per costruirla dal modello) e troppi vantaggi.
doc

4
Un commento all'OP: penso che l'attuale risposta accettata sia già obsoleta al punto da essere effettivamente sbagliata. Potresti voler riconsiderare la scelta della risposta accettata.
Ulidtko,

8
@GerardoMarset Sei serio?
thesaint

Risposte:


43

Perché non è ancora standard. Qualsiasi cosa non standard ha un ostacolo molto più elevato. Pezzi di Boost sono diventati popolari perché i programmatori chiedevano a gran voce per loro. (hash_map balza in mente). Ma mentre la tupla è utile, non è una vittoria così schiacciante e chiara che la gente si preoccupi di ciò.


1
Le persone sembrano usare altre parti di Boost come un matto. Sebbene certamente le mappe hash siano molto più utili in generale delle tuple.
Zifre,

Non conosco i dettagli di ciò che stai vedendo, ma immagino che le parti che le persone usano come un matto siano caratteristiche che volevano davvero. Quindi (di nuovo, indovinando) la popolarità della mappa hash, del puntatore contato e simili. La tupla è utile, ma non è qualcosa che salta fuori per riempire un buco. Il payoff non è ovvio. Quanto spesso devi ruotare esattamente N oggetti? (Al contrario della necessità di ruotare vettori arbitrariamente lunghi). E le persone sono abituate a passare valori di ritorno per riferimento o a restituire classi o strutture di piccole dimensioni.
Alan De Smet,

20
Attualmente fa parte dello standard C ++ 11: en.cppreference.com/w/cpp/utility/tuple
Roman Susi,

124

Una risposta cinica è che molte persone programmano in C ++, ma non comprendono e / o utilizzano le funzionalità di livello superiore. A volte è perché non sono ammessi, ma molti semplicemente non ci provano (o addirittura non capiscono).

Come esempio non boost: quante persone usano la funzionalità presente in <algorithm> ?

In altre parole, molti programmatori C ++ sono semplicemente programmatori C che usano compilatori C ++ e forse std::vectore std::list. Questo è uno dei motivi per cui l'uso di boost::tuplenon è più comune.


18
-1 da me perché i programmatori C ++ non sono così stupidi come questa risposta li fa sembrare.
user541686

5
@Mehrdad Dopo aver esaminato un sacco di codice C ++, sia commerciale che non, leggendo tonnellate di materiale C ++, penso che sia abbastanza sicuro dire che una porzione molto grande di sviluppatori "C ++" sono solo sviluppatori C che non sono in grado di ottenere un puro Compilatore C. I modelli, ad esempio, mancano quasi del tutto dalla maggior parte dei materiali (qualcosa che ho imparato ad amare molto). Strani hack di macro sono comuni e lo spazio dei nomi è gravemente sottoutilizzato.
Più chiaro il

5
Risposta assurda. Se uno ha bisogno di loro, li raggiungeranno. Non sono necessari; quindi non vengono utilizzati. Dire che non sono usati perché non sono facilmente comprensibili è male.
Michael Chourdakis,

9
@Michael Nonsense comment. Nulla è necessario nella programmazione dopo aver ottenuto un sottoinsieme completo di Turing di una lingua. La mancanza di utilizzo certamente non implica che tutti capiscano i costrutti C ++ di livello superiore e scelgano di non usarli.
Trey Jackson,

4
Tbh non ho mai avuto la necessità di std :: tuple al di fuori della metaprogrammazione del modello variadico. Inutile nella vita in cui mi sono seduto con una faccia triste pensando "Se solo avessi quelle tuple". In effetti, quando guardo le tuple, penso "Per cosa diavolo avrebbe bisogno qualcuno di sano (non mi considererei sano)". Le persone al di fuori della meta-programmazione sembrano usarle come "Anonimi Struct" che è così brutto e indica una forte assenza di qualità del codice e di manutenibilità nelle loro teste.
thesaint

23

La sintassi della tupla C ++ può essere un po 'più dettagliata di quanto la maggior parte delle persone vorrebbe.

Tener conto di:

typedef boost::tuple<MyClass1,MyClass2,MyClass3> MyTuple;

Quindi, se si desidera fare un ampio uso delle tuple, si ottengono typedef di tuple ovunque o si ottengono nomi di tipi fastidiosamente lunghi ovunque. Mi piacciono le tuple. Li uso quando necessario. Ma di solito è limitato a un paio di situazioni, come un indice N-element o quando si usano le multimap per legare le coppie di iteratori di intervallo. Ed è di solito in un ambito molto limitato.

È tutto molto brutto e burrascoso rispetto a qualcosa come Haskell o Python. Quando C ++ 0x arriva e otteniamo la parola chiave "auto", le tuple inizieranno a sembrare molto più attraenti.

L'utilità delle tuple è inversamente proporzionale al numero di sequenze di tasti richieste per dichiararle, comprimerle e decomprimerle.


Molte persone faranno "usando il potenziamento dello spazio dei nomi;" e non è necessario digitare boost ::. Non penso che scrivere tupla sia un grosso problema. Detto questo, penso che tu abbia ragione. auto potrebbe far sì che molte più persone inizino a usare le tuple.
Zifre,

2
@Zifre: il problema è che non dovresti "usare lo spazio dei nomi X" all'interno di un file di intestazione perché forza l'inquinamento dello spazio dei nomi e sovverte gli spazi dei nomi.
Fooz,

1
Ah, sì, mi dimentico delle intestazioni. Ma all'interno del codice del programma, non devi preoccuparti. E una volta che abbiamo C ++ 0x, possiamo usare auto che dovrebbe eliminare gran parte della digitazione.
Zifre,

19
Sono solo io? Non credo che il salvataggio di digitare i 7 caratteri di "boost ::" sia ciò a cui si riferiva, ma piuttosto gli altri 33 caratteri . Questo è un sacco di tipizzazione di molti nomi di classe, soprattutto se anche quelli hanno uno spazio dei nomi. Prendi boost :: tuple <std :: string, std :: set <std :: string>, std :: vector <My :: Scoped :: LongishTypeName>> come esempio ridicolo.
Ogre Salmo33

10

Per me è un'abitudine, senza dubbio: le tuple non risolvono alcun nuovo problema per me, solo alcune sono già in grado di gestire. Lo scambio di valori è ancora più semplice alla vecchia maniera - e, cosa più importante, non penso davvero a come scambiare "meglio". È abbastanza buono così com'è.

Personalmente, non credo che le tuple siano un'ottima soluzione per restituire più valori - sembra un lavoro per structs.


4
'Non penso davvero a come scambiare "meglio". "- Quando scrivo codice, scrivo bug. La riduzione della complessità del codice riduce il numero di bug che scrivo. Odio creare gli stessi bug ancora e ancora. Sì, penso a come <strike> scambiare meglio il codice </> . Meno parti mobili (LOC, variabili temporanee, identificatori da digitare male), codice più leggibile; Buon codice.
vedi il

Essere d'accordo. Una classe racchiusa nel puntatore automatico o in un puntatore intelligente è tipo salva. Ho usato le tuple una volta, ma poi ho riscritto il codice usando le classi. retValue.state è più chiaro di retValue.get <0> ().
Valentin Heinitz,

1
@sehe: scrivere meglio, codice più leggibile è anche il mio obiettivo. L'aggiunta di più tipi di sintassi ha un costo e non credo che uno "scambio migliore" giustifichi il sovraccarico mentale di pensare a un numero ancora maggiore di sintassi per ogni riga di codice che leggi.
ojrac,

8

Ma cosa succede se si desidera ruotare tre valori?

swap(a,b);
swap(b,c);  // I knew those permutation theory lectures would come in handy.

OK, quindi con 4 valori ecc., Alla fine la n-tupla diventa meno codice degli n-1 swap. E con lo scambio di default questo fa 6 compiti invece dei 4 che avresti se implementassi tu stesso un modello a tre cicli, anche se spero che il compilatore lo risolva per tipi semplici.

Puoi creare scenari in cui gli swap sono ingombranti o inappropriati, ad esempio:

tie(a,b,c) = make_tuple(b*c,a*c,a*b);

è un po 'scomodo da decomprimere.

Il punto è, tuttavia, che ci sono modi noti di affrontare le situazioni più comuni per le quali le tuple sono utili, e quindi nessuna grande urgenza di affrontare le tuple. Se non altro, non sono sicuro che:

tie(a,b,c) = make_tuple(b,c,a);

non esegue 6 copie, il che lo rende del tutto inadatto per alcuni tipi (le raccolte sono le più ovvie). Sentiti libero di convincermi che le tuple sono una buona idea per i tipi "grandi", dicendo che non è così :-)

Per restituire più valori, le tuple sono perfette se i valori sono di tipo incompatibile, ma ad alcune persone non piacciono se è possibile che il chiamante li porti nell'ordine sbagliato. Ad alcune persone non piacciono affatto i valori di ritorno multipli e non vogliono incoraggiarne l'uso rendendoli più facili. Alcune persone preferiscono semplicemente strutture nominate per i parametri di entrata e uscita e probabilmente non potrebbero essere persuase con una mazza da baseball per usare le tuple. Nessuna considerazione per il gusto.


1
Non vorrai assolutamente scambiare vettori con tuple. Penso che scambiare tre elementi sia certamente più chiaro con le tuple che con due swap. Per quanto riguarda i valori di ritorno multipli, i parametri out sono malvagi, le strutture hanno una digitazione extra e ci sono sicuramente casi in cui sono necessari più valori di ritorno.
Zifre,

sapere perché si usa tuple (e so perché io li userei se l'occasione si alzò, anche se non credo che abbia mai fatto). Immagino perché gli altri non li usano anche se ne sono consapevoli. ad esempio perché non sono d'accordo con "i params sono cattivi" ...
Steve Jessop,

Sai se possiamo sostituire "tie (a, b, c) = make_tuple (b, c, a);" di "tie (a, b, c) = tie (b, c, a);" ?
Rexxar,

2
Un pareggio (bene, tecnicamente un livello) è una tupla fatta con riferimenti non costanti. Non riesco a trovare la documentazione di boost che dica cosa garantisce l'operatore = e il costruttore della copia per tie / tuple quando alcuni dei riferimenti coinvolti hanno lo stesso riferimento. Ma è quello che devi sapere. Un'implementazione ingenua dell'operatore = chiaramente potrebbe andare molto male ...
Steve Jessop,

1
@Steve: E poiché i legami riguardano la prevenzione delle copie (dovrebbero funzionare per tipi non copiabili; nota che l'LHS potrebbe essere qualcos'altro del tutto), in effetti tutto dovrebbe andare molto male (pensa agli oggetti di classe non POD). Immagina come scrivere la stessa logica senza usare temps.
vedi il

7

Come molte persone hanno sottolineato, le tuple non sono così utili come altre funzionalità.

  1. Gli espedienti di scambio e rotazione sono solo espedienti. Sono assolutamente confusi per coloro che non li hanno mai visti prima, e poiché sono praticamente tutti, questi espedienti sono solo una cattiva pratica di ingegneria del software.

  2. Restituire più valori usando le tuple è molto meno auto-documentante delle alternative: restituire tipi nominati o usare riferimenti nominati. Senza questa auto-documentazione, è facile confondere l'ordine dei valori restituiti, se sono reciprocamente convertibili e non essere più saggi.


6

Non tutti possono usare boost e TR1 non è ancora ampiamente disponibile.


3
Molte persone usano Boost. Quelle persone potrebbero usare anche le tuple.
Zifre,

3
Mi hai chiesto perché le persone non le usano e io ho dato una risposta.
Brian Neal,

2
Per il down voter: mi capita di lavorare in un posto in cui è politicamente impossibile usare boost, e anche a questa data, la catena di strumenti di compilazione che utilizziamo (per un sistema incorporato) non ha il supporto TR1 / C ++ 11.
Brian Neal,

5

Quando si utilizza C ++ su sistemi embedded, l'estrazione delle librerie Boost diventa complessa. Si accoppiano l'un l'altro, quindi la dimensione della libreria aumenta. Restituisci strutture di dati o usi il passaggio di parametri anziché le tuple. Quando si restituiscono tuple in Python, la struttura dei dati è nell'ordine e il tipo dei valori restituiti è semplicemente non esplicito.


5

Raramente li vedi perché il codice ben progettato di solito non ne ha bisogno, non ci sono molti casi in natura in cui l'uso di una struttura anonima è superiore all'utilizzo di uno con nome. Poiché tutto ciò che una tupla rappresenta in realtà è una struttura anonima, la maggior parte dei programmatori nella maggior parte delle situazioni si adatta semplicemente alla cosa reale.

Supponiamo che abbiamo una funzione "f" in cui un ritorno di tupla potrebbe avere senso. Come regola generale, tali funzioni sono di solito abbastanza complicate da poter fallire.

Se "f" PU fail fallire, hai bisogno di un ritorno di stato - dopotutto, non vuoi che i chiamanti debbano ispezionare ogni parametro per rilevare un errore. "f" probabilmente si adatta al modello:

struct ReturnInts ( int y,z; }
bool f(int x, ReturnInts& vals);

int x = 0;
ReturnInts vals;
if(!f(x, vals)) {
    ..report error..
    ..error handling/return...
}

Non è carino, ma guarda quanto brutta è l'alternativa. Nota che ho ancora bisogno di un valore di stato, ma il codice non è più leggibile e non più breve. Probabilmente è anche più lento, poiché incorre nel costo di 1 copia con la tupla.

std::tuple<int, int, bool> f(int x);
int x = 0;
std::tuple<int, int, bool> result = f(x); // or "auto result = f(x)"
if(!result.get<2>()) {
    ... report error, error handling ...
}

Un altro svantaggio significativo è nascosto qui: con "ReturnInts" posso aggiungere il ritorno dell'alter "f" modificando "ReturnInts" SENZA ALTERARE L'INTERFACCIA "f". La soluzione tupla non offre quella caratteristica critica, che la rende la risposta inferiore per qualsiasi codice di libreria.


1
Le eccezioni rendono l'interfaccia molto più pulita.
David Stone,

Per essere onesti (ed estremamente in ritardo con la festa) potresti facilitare la leggibilità impostando using std::tuple;e semplicemente usando tuplenel codice.
Alrekr,

2
L'uso tuplerende il codice meno leggibile, non di più. La maggior parte dei codici in questi giorni ha un numero molto grande di simboli: vedere std::tuplechiarisce esattamente di cosa si tratta.
Tom Swirly,

3

Sicuramente le tuple possono essere utili, ma come detto c'è un po 'di sovraccarico e un paio di ostacoli che devi saltare prima di poterli usare davvero.

Se il tuo programma trova costantemente luoghi in cui devi restituire più valori o scambiare più valori, potrebbe valerne la pena percorrere la strada delle tuple, ma a volte è più semplice fare le cose nel modo classico.

In generale, non tutti hanno già installato Boost, e di certo non passerei la seccatura di scaricarlo e configurare le mie directory include per lavorarci solo per le sue strutture di tupla. Penso che scoprirai che le persone che già utilizzano Boost hanno maggiori probabilità di trovare usi tupla nei loro programmi rispetto agli utenti non Boost e che i migranti da altre lingue (viene in mente Python) hanno maggiori probabilità di essere semplicemente arrabbiati per la mancanza di tuple in C ++ piuttosto che esplorare i metodi per aggiungere il supporto tupla.


1

Poiché un archivio di dati std::tuplepresenta le peggiori caratteristiche di a structe di un array; tutti gli accessi sono basati sull'ennesima posizione ma non si può iterare tupleusando afor ciclo.

Quindi, se gli elementi in tuplesono concettualmente una matrice, userò una matrice e se gli elementi non sono concettualmente una matrice, una struttura (che ha denominato elementi) è più mantenibile. ( a.lastnameè più esplicativo di std::get<1>(a)).

Questo lascia la trasformazione menzionata dall'OP come unica base utilizzabile per le tuple.


0

Ho la sensazione che molti usano Boost.Any e Boost.Variant (con un po 'di ingegneria) invece di Boost.Tuple.


Perché dovresti scambiare una tipizzazione statica efficiente con qualcosa del genere?
Zifre,

Boost.Variant è completamente sicuro per i tipi.
user21714,

1
Oops, sì è typesafe, ma esegue la digitazione in fase di esecuzione.
Zifre,

5
Non vedo come Tuple possa essere sostituito Any / Variant. Non fanno la stessa cosa.
Mankarse,

1
@Zifre Non posso parlare per l'autore, ma penso che le implicazioni qui li stiano usando insieme ad altri tipi di container.
Tim Seguine,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.