Auto rende il codice C ++ più difficile da capire?


122

Ho visto una conferenza di Herb Sutter in cui incoraggia ogni programmatore C ++ a usare auto.

Ho dovuto leggere il codice C # qualche tempo fa dove varera ampiamente utilizzato e il codice era molto difficile da capire - ogni volta che varveniva usato dovevo controllare il tipo di ritorno sul lato destro. A volte più di una volta, perché dopo un po 'ho dimenticato il tipo di variabile!

So che il compilatore conosce il tipo e non devo scriverlo, ma è ampiamente accettato che dovremmo scrivere codice per programmatori, non per compilatori.

So anche che è più facile scrivere:

auto x = GetX();

Di:

someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();

Ma questo viene scritto solo una volta e il GetX()tipo restituito viene verificato più volte per capire quale tipo xha.

Questo mi ha fatto meravigliare: autorende il codice C ++ più difficile da capire?


29
Devi davvero controllare il tipo di reso ogni volta ? Perché il tipo non è chiaro dal codice? autospesso rende le cose più difficili da leggere quando sono già difficili da leggere, cioè funzioni troppo lunghe, variabili con un nome mediocre, ecc. Nelle funzioni brevi con variabili con un nome decente, sapere che i tipi dovrebbero essere uno dei n. 1 facili o n. 2 irrilevanti.
R. Martinho Fernandes,

25
L '"arte" dell'uso autoè molto simile a determinare quando usare typedef. Sta a te decidere quando ostacola e quando aiuta.
ahenderson,

18
Pensavo di avere lo stesso problema, ma poi mi sono reso conto che posso solo capire il codice senza conoscere i tipi. ad esempio: "auto idx = get_index ();" quindi idx è qualcosa che contiene un indice. Qual è il tipo esatto, è abbastanza irrilevante nella maggior parte dei casi.
PlasmaHH,

31
Quindi non scrivere auto x = GetX();, scegli un nome migliore di quello xche in realtà ti dice cosa fa in quel contesto specifico ... che è spesso più utile del suo tipo comunque.
Jonathan Wakely,

11
Se l'utilizzo di più inferenze di tipo rende difficile per un programmatore la lettura del codice, il codice o il programmatore devono essere notevolmente migliorati.
CA McCann,

Risposte:


100

Risposta breve: Più completamente, la mia attuale opinione autoè che dovresti usare autodi default a meno che tu non voglia esplicitamente una conversione. (Leggermente più precisamente, "... a meno che tu non voglia impegnarti esplicitamente in un tipo, che quasi sempre è perché vuoi una conversione.")

Risposta e motivazione più lunghe:

Scrivi un tipo esplicito (piuttosto che auto) solo quando vuoi veramente impegnarti esplicitamente in un tipo, il che significa quasi sempre che vuoi ottenere esplicitamente una conversione in quel tipo. Dall'alto della mia testa, ricordo due casi principali:

  • (Comune) La initializer_listsorpresa che auto x = { 1 };deduce initializer_list. Se non vuoi initializer_list, dì il tipo, ovvero chiedi esplicitamente una conversione.
  • (Raro) Il caso dei modelli di espressione, come quello che auto x = matrix1 * matrix 2 + matrix3;acquisisce un tipo di supporto o proxy non destinato a essere visibile al programmatore. In molti casi, è bene e benigno catturare quel tipo, ma a volte se vuoi davvero che collassi e faccia il calcolo, allora dì il tipo - cioè, ancora una volta esplicitamente chiedere una conversione.

In caso autocontrario, utilizzare di routine in modo predefinito, perché l'uso autoevita le insidie ​​e rende il codice più corretto, più gestibile, robusto e più efficiente. Circa nell'ordine dal più importante al meno importante, nello spirito di "scrivi prima per chiarezza e correttezza":

  • Correttezza: usando le autogaranzie otterrai il tipo giusto. Come dice il proverbio, se ti ripeti (pronuncia il tipo in modo ridondante), puoi e mentirai (sbagli). Ecco un esempio usuale: void f( const vector<int>& v ) { for( /*…*- a questo punto, se scrivi esplicitamente il tipo di iteratore, vuoi ricordarti di scrivere const_iterator(vero?), Mentre autolo fa bene.
  • Mantenibilità e robustezza: l' utilizzo autorende il codice più robusto di fronte al cambiamento, perché quando il tipo di espressione cambia, autocontinuerà a risolversi nel tipo corretto. Se invece ti impegni con un tipo esplicito, la modifica del tipo dell'espressione genererà conversioni silenziose quando il nuovo tipo viene convertito nel vecchio tipo o interruzioni di build inutili quando il nuovo tipo funziona ancora come il vecchio ma non converte nel vecchio digitare (ad esempio, quando si cambia a mapin un unordered_map, il che va sempre bene se non si fa affidamento sull'ordine, usando autoper i propri iteratori si passa senza interruzioni da map<>::iteratora unordered_map<>::iterator, ma usandomap<>::iterator ovunque significa esplicitamente che stai sprecando il tuo tempo prezioso in un'ondulazione di correzione del codice meccanico, a meno che un tirocinante non passi e tu possa evitare il noioso lavoro su di loro).
  • Prestazioni: poiché autogarantisce che non si verifichi alcuna conversione implicita, garantisce prestazioni migliori per impostazione predefinita. Se invece dici il tipo e richiede una conversione, otterrai spesso una conversione in silenzio, indipendentemente dal fatto che te lo aspetti.
  • Usabilità: l' utilizzo autoè l'unica buona opzione per tipi difficili da scrivere e indecifrabili, come lambda e aiutanti di template, a corto di ricorrere a decltypeespressioni ripetitive o indirette meno efficienti come std::function.
  • Convenienza: e, sì, autoè meno digitando. Ne parlo per completezza perché è un motivo comune per apprezzarlo, ma non è il motivo principale per usarlo.

Quindi: preferisco dire autodi default. Offre così tanta semplicità, prestazioni e chiarezza che ti danneggerai solo (e i futuri manutentori del tuo codice) se non lo fai. Impegnarsi in un tipo esplicito solo quando lo si intende davvero, il che significa quasi sempre che si desidera una conversione esplicita.

Sì, c'è (ora) un GotW su questo.


14
Trovo l'auto utile anche quando voglio una conversione. Mi permette di chiedere in modo esplicito per una conversione senza ripetere il tipo: auto x = static_cast<X>(y). La static_castprecisa che la conversione è di proposito ed evita gli avvisi del compilatore per la conversione. Normalmente evitare gli avvisi del compilatore non è così buono, ma sto bene non ricevere un avviso su una conversione che ho considerato attentamente quando ho scritto static_cast. Anche se non lo farei se non ci sono avvisi ora, ma voglio ricevere avvisi in futuro se i tipi cambiano in modo potenzialmente pericoloso.
Bjarke Hammersholt Roune,

6
Una cosa che trovo autoè che dovremmo cercare di programmare contro interfacce (non nel senso di OOP), non contro implementazioni specifiche. È lo stesso con i modelli, davvero. Ti lamenti del "codice difficile da leggere" perché hai un parametro del tipo di modello Tche viene utilizzato ovunque? No, non la penso così. Anche nei template codifichiamo un'interfaccia, la tipizzazione duck duck in fase di compilazione è ciò che la chiamano molte persone.
Xeo,

6
"Usando le garanzie automatiche otterrai il tipo giusto." Per niente vero. Garantisce solo che otterrai il tipo prescritto da qualche altra parte del tuo codice. Se è giusto o no non è del tutto chiaro quando lo si nasconde dietro auto.
Razze di leggerezza in orbita

Sono davvero sorpreso che a nessuno importi degli IDE ... Anche gli IDE moderni non supportano correttamente il salto alla definizione di classe / struttura in caso di autovariabile, ma quasi tutti lo fanno correttamente con specifiche di tipo esplicite. Nessuno usa IDE? Tutti usano solo variabili int / float / bool? Tutti preferiscono la documentazione esterna per le biblioteche anziché le intestazioni autodocumentate?
Avtomaton,

che GotW: herbsutter.com/2013/08/12/… Non vedo come quella "sorpresa inizialista_elenco" sia una sorpresa; le parentesi graffe intorno a =RHS non hanno molto senso da qualsiasi altra interpretazione (barra di inizializzazione rinforzata, ma è necessario sapere che cosa si sta inizializzando, che è un ossimoro auto). Quello che è sorprendente è auto i{1}anche deducendo initializer_list, nonostante il che implica non prendere questa preparò init-list , ma piuttosto prendere questa espressione e di utilizzare il suo tipo ... ma otteniamo initializer_listanche lì. Per fortuna, C ++ 17 risolve tutto bene.
underscore_d

112

È una situazione caso per caso.

A volte rende il codice più difficile da capire, a volte no. Prendi ad esempio:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

è sicuramente facile da capire e sicuramente più facile da scrivere rispetto alla dichiarazione dell'iteratore reale.

Sto usando C ++ da un po 'di tempo, ma posso garantire che al mio primo colpo avrei ricevuto un errore del compilatore perché mi sarei dimenticato del const_iteratore inizialmente avrei scelto il iterator... :)

Lo userei per casi come questo, ma non dove effettivamente offusca il tipo (come la tua situazione), ma questo è puramente soggettivo.


45
Esattamente. A chi diavolo importa del tipo. È un iteratore. Non mi interessa il tipo, tutto quello che devo sapere è che posso usarlo per iterare.
R. Martinho Fernandes,

5
+1. Anche se hai chiamato il tipo, lo chiameresti come std::map<int, std::string>::const_iterator, quindi non è come se il nome ti dicesse molto sul tipo comunque.
Steve Jessop,

4
@SteveJessop: mi dice almeno due cose: la chiave è inte il valore è std::string. :)
Nawaz,

16
@Nawaz: e che non puoi assegnare it->secondpoiché è un costatore. Tutto ciò le informazioni è una ripetizione di ciò che è nella riga precedente, const std::map<int, std::string>& x. Dire cose più volte ogni tanto informa meglio, ma non è affatto una regola generale :-)
Steve Jessop,

11
TBH Preferirei for (anX : x)rendere ancora più ovvio che stiamo solo ripetendo x. Il caso normale in cui è necessario un iteratore è quando si modifica il contenitore, ma xèconst&
MSalters il

94

Guarda in un altro modo. Scrivi:

std::cout << (foo() + bar()) << "\n";

o:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

A volte non aiuta a precisare esplicitamente il tipo.

La decisione se è necessario menzionare il tipo non è la stessa decisione se si desidera dividere il codice su più istruzioni definendo variabili intermedie. In C ++ 03 i due erano collegati, puoi pensare autoa un modo per separarli.

A volte rendere espliciti i tipi può essere utile:

// seems legit    
if (foo() < bar()) { ... }

vs.

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

Nei casi in cui dichiari una variabile, l'utilizzo autoconsente di lasciare il tipo non detto come in molte espressioni. Probabilmente dovresti provare a decidere da solo quando ciò aiuta la leggibilità e quando ostacola.

Si può sostenere che la combinazione di tipi firmati e non firmati sia un errore per cominciare (in effetti, alcuni sostengono inoltre che non si dovrebbero usare affatto tipi non firmati). La ragione per cui è probabilmente un errore è che rende i tipi di operandi di vitale importanza a causa del diverso comportamento. Se è una brutta cosa conoscere i tipi dei tuoi valori, allora probabilmente non è neanche una brutta cosa non aver bisogno di conoscerli. Quindi, a condizione che il codice non sia già confuso per altri motivi, ciò rende autoOK, giusto? ;-)

Soprattutto quando si scrive codice generico ci sono casi in cui il tipo effettivo di una variabile non dovrebbe essere importante, ciò che conta è che soddisfa l'interfaccia richiesta. Quindi autofornisce un livello di astrazione in cui ignori il tipo (ma ovviamente il compilatore non lo sa, lo sa). Lavorare a un livello di astrazione adeguato può aiutare parecchio la leggibilità, lavorare a un livello "sbagliato" rende la lettura del codice un errore.


21
+1 autoti consente di creare variabili con nome con tipi innominabili o non interessanti. I nomi significativi possono essere utili.
Mankarse,

Miscelazione firmata e non firmata se si utilizza unsigned per l'uso corretto: aritmetica modulare. Non è se usi impropriamente unsigned per intero positivo. Quasi nessun programma ha un uso per unsigned, ma il linguaggio principale impone la sua definizione insensata di sizeofunsigned su di te.
curiousguy,

27

IMO, lo stai guardando praticamente al contrario.

Non si tratta di autoportare a codice illeggibile o ancor meno leggibile. Si tratta di (sperando che) avere un tipo esplicito per il valore restituito compensi il fatto che (apparentemente) non è chiaro quale tipo sarebbe restituito da una particolare funzione.

Almeno secondo me, se hai una funzione il cui tipo di ritorno non è immediatamente evidente, questo è il tuo problema proprio lì. Ciò che la funzione fa dovrebbe essere ovvio dal suo nome e il tipo di valore restituito dovrebbe essere ovvio da ciò che fa. In caso contrario, questa è la vera fonte del problema.

Se c'è un problema qui, non è con auto. È con il resto del codice e ci sono buone possibilità che il tipo esplicito sia appena sufficiente per un cerotto per impedirti di vedere e / o risolvere il problema principale. Una volta risolto il problema, la leggibilità del codice autoin genere andrà bene.

Immagino che, in tutta onestà, dovrei aggiungere: ho affrontato alcuni casi in cui tali cose non erano così ovvie come vorresti, e anche risolvere il problema era abbastanza insostenibile. Solo per fare un esempio, un paio di anni fa ho fatto consulenza per un'azienda che in precedenza si era fusa con un'altra società. Hanno finito con una base di codice che era più "messa insieme" di quanto realmente fusa. I programmi costituenti avevano iniziato usando librerie diverse (ma abbastanza simili) per scopi simili, e sebbene stessero lavorando per unire le cose in modo più pulito, lo facevano comunque. In un discreto numero di casi, l'unico modo per indovinare quale tipo sarebbe stato restituito da una determinata funzione era sapere dove aveva avuto origine quella funzione.

Anche in questo caso, puoi aiutare a chiarire alcune cose. In tal caso, tutto il codice è iniziato nello spazio dei nomi globale. Il semplice spostamento di una discreta quantità in alcuni spazi dei nomi ha eliminato gli scontri con i nomi e facilitato un po 'anche il monitoraggio del tipo.


17

Esistono diversi motivi per cui non mi piace l'auto per uso generale:

  1. Puoi refactificare il codice senza modificarlo. Sì, questa è una delle cose spesso elencate come un vantaggio dell'uso di auto. Basta cambiare il tipo di ritorno di una funzione e se tutto il codice che la chiama utilizza auto, non è richiesto alcuno sforzo aggiuntivo! Fai clic su compila, crea - 0 avvisi, 0 errori - e vai avanti e controlla il tuo codice senza dover affrontare il disordine di guardare attraverso e modificare potenzialmente gli 80 luoghi in cui viene utilizzata la funzione.

Ma aspetta, è davvero una buona idea? E se il tipo avesse importanza in una mezza dozzina di quei casi d'uso, e ora quel codice si comporta effettivamente diversamente? Anche questo può implicitamente interrompere l'incapsulamento, modificando non solo i valori di input, ma il comportamento stesso dell'implementazione privata di altre classi che chiamano la funzione.

1 bis. Sono un sostenitore del concetto di "codice auto-documentante". Il ragionamento alla base del codice auto-documentante è che i commenti tendono a diventare obsoleti, non riflettendo più ciò che il codice sta facendo, mentre il codice stesso - se scritto in modo esplicito - è autoesplicativo, rimane sempre aggiornato sul suo intento e non ti lascerà confuso con i commenti non aggiornati. Se i tipi possono essere cambiati senza la necessità di modificare il codice stesso, allora il codice / le variabili stesse possono diventare obsoleti. Per esempio:

auto bThreadOK = CheckThreadHealth ();

Tranne il problema è che CheckThreadHealth () ad un certo punto è stato refactored per restituire un valore enum che indica lo stato dell'errore, se presente, invece di un bool. Ma la persona che ha apportato tale modifica ha perso l'ispezione di questa particolare riga di codice e il compilatore non è stato di alcun aiuto poiché è stato compilato senza avvisi o errori.

  1. Potresti non sapere mai quali sono i tipi reali. Questo è anche spesso elencato come un "vantaggio" primario di auto. Perché imparare quale funzione ti sta dando, quando puoi semplicemente dire "A chi importa? Si compila!"

Funziona anche in qualche modo, probabilmente. Dico un po 'di lavori, perché anche se stai facendo una copia di una struttura di 500 byte per ogni iterazione di loop, in modo da poter ispezionare un singolo valore su di esso, il codice è ancora completamente funzionale. Quindi anche i test unitari non ti aiutano a capire che un codice errato si nasconde dietro quell'auto semplice e dall'aspetto innocente. La maggior parte delle altre persone che eseguono la scansione del file non lo noteranno a prima vista.

Ciò può anche peggiorare se non si conosce il tipo, ma si sceglie un nome di variabile che fa un'ipotesi errata su ciò che è, ottenendo in effetti lo stesso risultato di 1a, ma dall'inizio piuttosto che post-refactoring.

  1. Digitare il codice durante la scrittura iniziale non è la parte più lunga della programmazione. Sì, inizialmente la scrittura automatica rende più veloce il codice. Come disclaimer, digito> 100 WPM, quindi forse non mi disturba tanto quanto gli altri. Ma se tutto ciò che dovevo fare era scrivere un nuovo codice tutto il giorno, sarei un campeggiatore felice. La parte della programmazione che richiede più tempo è la diagnosi di bug nel codice difficili da riprodurre, che spesso derivano da problemi non ovvi e sottili, come è probabile che venga introdotto il tipo di uso eccessivo di auto (riferimento vs. copia, firmato vs. non firmato, float vs. int, bool vs. pointer, ecc.).

Mi sembra ovvio che auto sia stata introdotta principalmente come soluzione per terribile sintassi con tipi di template di libreria standard. Invece di provare a correggere la sintassi del modello con cui le persone hanno già familiarità - che potrebbe anche essere quasi impossibile da fare a causa di tutto il codice esistente che potrebbe rompere - aggiungere una parola chiave che sostanzialmente nasconde il problema. In sostanza quello che potresti chiamare un "hack".

In realtà non ho alcun disaccordo con l'uso dell'auto con i contenitori standard delle librerie. È ovviamente ciò per cui è stata creata la parola chiave e le funzioni nella libreria standard non cambieranno sostanzialmente lo scopo (o il tipo per quella materia), rendendo l'auto relativamente sicura da usare. Ma sarei molto cauto nell'usarlo con il tuo codice e interfacce che potrebbero essere molto più volatili e potenzialmente soggetti a cambiamenti più fondamentali.

Un'altra utile applicazione di auto che migliora la capacità del linguaggio è la creazione di provvisori in macro di tipo agnostico. Questo è qualcosa che non potevi davvero fare prima, ma puoi farlo ora.


4
L'hai inchiodato. Vorrei potergli dare un +2.
cmaster

Una buona risposta "sii dannatamente cauto". @cmaster: eccolo.
Deduplicatore,

Ho trovato più utile un caso: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist

14

Sì, è più facile conoscere il tipo di variabile se non la usi auto. La domanda è: cosa è necessario conoscere il tipo di variabile per leggere il codice? A volte la risposta sarà sì, a volte no. Ad esempio, quando si ottiene un iteratore da un std::vector<int>, è necessario sapere che è un std::vector<int>::iteratoro sarebbe auto iterator = ...;sufficiente? Tutto ciò che chiunque vorrebbe fare con un iteratore è dato dal fatto che è un iteratore - non importa quale sia il tipo in particolare.

Utilizzare autoin quelle situazioni in cui non è più difficile leggere il codice.


12

Personalmente uso autosolo quando è assolutamente ovvio per il programmatore di cosa si tratta.

Esempio 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Esempio 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

5
Questo è un chiaro esempio di cattiva denominazione che danneggia la leggibilità, non proprio automatica. Nessuno ha la più pallida idea di cosa faccia "DoSomethingWeird", quindi l'uso di auto o no non lo renderà più leggibile. Dovrai controllare i documenti in entrambi i modi.
R. Martinho Fernandes,

4
Ok, ora è un po 'meglio. Trovo comunque che la variabile sia scarsamente chiamata, ma che fa ancora male. Se dovessi scrivere auto record = myObj.FindRecord(something), sarebbe chiaro che il tipo di variabile era record. O nominarlo ito simile chiarirebbe che restituisce un iteratore. Si noti che, anche se non si è utilizzato auto, la corretta denominazione della variabile significherebbe che non è necessario tornare indietro alla dichiarazione per esaminare il tipo da qualsiasi punto della funzione . Ho rimosso il mio downvote perché l'esempio non è un uomo di paglia completo ora, ma non compro ancora l'argomento qui.
R. Martinho Fernandes,

2
Per aggiungere a @ R.MartinhoFernandes: la domanda è, è davvero importante ora CHE COSA è esattamente un "record"? Penso che sia più importante CHE sia un record, l'attuale tipo primitivo sottostante è un altro strato di astrazione .. Quindi uno senza auto probabilmente avrebbe:MyClass::RecordTy record = myObj.FindRecord (something)
pa2323

2
@ paul23: Cosa ti guadagna usando auto contro il tipo, quindi, se la tua unica obiezione è "Non so come usarlo". O ti fa cercare comunque.
GManNickG,

3
@GManNickG mi dice il tipo esatto di non importanza.
pa23,

10

Questa domanda sollecita l'opinione, che varierà da programmatore a programmatore, ma direi di no. In effetti, in molti casi esattamente il contrario, autopuò aiutare a rendere il codice più facile da capire, consentendo al programmatore di concentrarsi sulla logica piuttosto che sulle minuzie.

Ciò è particolarmente vero di fronte a tipi di modelli complessi. Ecco un esempio semplificato e inventato. Quale è più facile da capire?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. o...

for( auto it = things_.begin(); it != things_.end(); ++it )

Alcuni direbbero che il secondo è più facile da capire, altri potrebbero dire il primo. Altri, tuttavia, potrebbero dire che un uso gratuito di autopuò contribuire a smorzare i programmatori che lo usano, ma questa è un'altra storia.


4
+1 Haha, tutti presentano std::mapesempi, inoltre con argomenti modello complessi.
Nawaz,

1
@Nawaz: è facile trovare nomi di modelli pazzi usando maps. :)
John Dibling,

@Nawaz: ma mi chiedo perché allora nessuno stia arrivando con la gamma basata sui loop come l'alternativa migliore e più leggibile ...
PlasmaHH

1
@PlasmaHH, non tutti i loop con iteratori possono essere sostituiti con range-based, forad esempio se gli iteratori sono invalidati nel corpo del loop e quindi devono essere pre-incrementati o non incrementati affatto.
Jonathan Wakely,

@PlasmaHH: Nel mio caso, MSVC10 non si basa sul range per i loop. Dato che MSVC10 è il mio testbed C ++ 11, non ho molta esperienza con loro.
John Dibling,

8

Molte buone risposte finora, ma per concentrarmi sulla domanda originale, penso che Herb esca troppo nei suoi consigli per usare autoliberamente. Il tuo esempio è un caso in cui l'utilizzo autoovviamente danneggia la leggibilità. Alcune persone insistono sul fatto che non è un problema con gli IDE moderni in cui è possibile passare il mouse sopra una variabile e vedere il tipo, ma non sono d'accordo: anche le persone che usano sempre un IDE a volte devono guardare frammenti di codice in modo isolato (pensa alle recensioni del codice , ad esempio) e un IDE non aiuta.

Bottom line: usare autoquando aiuta: vale a dire iteratori per i loop. Non usarlo quando fa fatica al lettore a scoprire il tipo.


6

Sono abbastanza sorpreso che nessuno abbia ancora sottolineato che l'auto aiuta se non esiste un tipo chiaro. In questo caso, o risolvi questo problema usando un #define o un typedef in un modello per trovare il tipo utilizzabile effettivo (e questo a volte non è banale), oppure usi semplicemente auto.

Supponiamo che tu abbia una funzione, che restituisce qualcosa con un tipo specifico della piattaforma:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

Preferiresti usare la strega?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

o semplicemente

auto stuff = getStuff();

Certo, puoi scrivere

#define StuffType (...)

anche da qualche parte, ma lo fa

StuffType stuff = getStuff();

in realtà dire qualcosa di più sul tipo di x? Dice che è ciò che viene restituito da lì, ma è esattamente ciò che è auto. Questo è semplicemente ridondante - "roba" è scritta 3 volte qui - questo secondo me lo rende meno leggibile rispetto alla versione "auto".


5
Il modo corretto di gestire i tipi specifici della piattaforma è per typedefloro.
cmaster

3

La leggibilità è soggettiva; dovrai guardare la situazione e decidere quale sia la migliore.

Come hai sottolineato, senza auto, le dichiarazioni lunghe possono produrre molto disordine. Ma come hai anche sottolineato, brevi dichiarazioni possono rimuovere informazioni sul tipo che possono essere utili.

Inoltre, aggiungerei anche questo: assicurati di guardare alla leggibilità e non alla scrivibilità. Il codice facile da scrivere non è generalmente facile da leggere e viceversa. Ad esempio, se stessi scrivendo, preferirei auto. Se stavo leggendo, forse le dichiarazioni più lunghe.

Quindi c'è coerenza; quanto è importante per te? Vorresti auto in alcune parti e dichiarazioni esplicite in altre o un metodo coerente in tutto?


2

Prenderò il punto del codice meno leggibile come un vantaggio e incoraggerò il programmatore a usarlo sempre di più. Perché? Chiaramente se il codice che utilizza auto è difficile da leggere, allora sarà difficile anche scrivere. Il programmatore è costretto a usare il nome della variabile significativa per migliorare il proprio lavoro.
Forse all'inizio il programmatore potrebbe non scrivere i nomi delle variabili significative. Ma alla fine, mentre corregge i bug, o nella revisione del codice, quando deve spiegare il codice ad altri, o in un prossimo futuro, spiegando il codice agli addetti alla manutenzione, il programmatore realizzerà l'errore e userà il nome della variabile significativa in futuro.


2
Nella migliore delle ipotesi, potresti convincere le persone a scrivere nomi di variabili come myComplexDerivedTypecompensare il tipo mancante, che ingombra il codice in base alla ripetizione del tipo (ovunque venga utilizzata la variabile) e che induce le persone a omettere lo scopo della variabile nel suo nome . La mia esperienza è che non c'è nulla di così improduttivo come mettere attivamente ostacoli nel codice.
cmaster

2

Ho due linee guida:

  • Se il tipo di variabile è ovvio, noioso da scrivere o difficile da determinare usare auto.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
  • Se è necessaria una conversione specifica o il tipo di risultato non è ovvio e potrebbe causare confusione.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious

0

Sì. Diminuisce la verbosità ma il malinteso comune è che la verbosità diminuisce la leggibilità. Questo è vero solo se consideri la leggibilità estetica piuttosto che la tua reale capacità di interpretare il codice, che non viene aumentata usando auto. Nell'esempio più comunemente citato, iteratori vettoriali, potrebbe sembrare in superficie che l'uso di auto aumenti la leggibilità del codice. D'altra parte, non sempre sai cosa ti darà la parola chiave auto. Devi seguire lo stesso percorso logico del compilatore per fare quella ricostruzione interna, e molte volte, in particolare con gli iteratori, farai ipotesi sbagliate.

Alla fine della giornata "auto" sacrifica la leggibilità del codice e la chiarezza, per la "pulizia" sintattica ed estetica (che è necessaria solo perché gli iteratori hanno una sintassi inutilmente contorta) e la possibilità di digitare 10 caratteri in meno su una determinata riga. Non vale la pena rischiare o lo sforzo a lungo termine.

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.