Perché alcune persone preferiscono “T const &” over “const T &”?

Quindi, mi rendo conto che const T& e T const& sono identici e significano entrambi un riferimento a un const T. In entrambi i casi, il riferimento è anche costante (i riferimenti non possono essere riassegnati, a differenza dei puntatori). Ho osservato, nella mia esperienza un po ‘limitata, che la maggior parte dei programmatori C ++ usa const T& , ma mi sono imbattuto in alcune persone che usano T const& . Io uso const T& semplicemente perché l’ho imparato in quel modo, e così T const& sembra un po ‘strano per me. Qual è la ragione per cui usi la variante che usi? Qualcuno di voi lavora presso un’organizzazione per la quale gli standard di codifica impongono l’uso di una variante rispetto all’altra?

modificare
Sulla base delle risposte, sembrerebbe che uno dei motivi per cui scegliere tra i due sia se si vuole leggerlo come il compilatore (da destra a sinistra) o come l’inglese (da sinistra a destra). Se lo si legge come il compilatore, quindi “T const &” legge come “& (riferimento) const (a una costante) T (di tipo T)”. Se la si legge come l’inglese, da sinistra a destra, quindi “const T &” viene letto come “un object costante di tipo T sotto forma di riferimento”. Preferisco leggerlo come una prosa inglese, ma posso certamente vedere il senso nell’interpretarlo come fa il compilatore.

Nessuno ha risposto all’organizzazione o alla domanda sugli standard di codifica, ma sospetto fortemente che la maggior parte delle organizzazioni non imponga l’una sull’altra, sebbene possano cercare coerenza.

Penso che alcune persone preferiscano semplicemente leggere le dichiarazioni da destra a sinistra. const applica al token di sinistra, tranne quando non c’è nulla e si applica al token di destra. Quindi, const T& implica la “eccetto” -clausa e può forse essere pensato più complicato (in realtà entrambi dovrebbero essere facili da capire).

Confrontare:

 const T* p; (pointer to T that is const) T const* p; (pointer to const T) //<- arguable more natural to read T* const p; (const pointer to T) 

Questo farà la differenza quando avrai più di un modificatore const / volatile. Quindi metterlo a sinistra del tipo è ancora valido ma interromperà la coerenza di tutta la dichiarazione. Per esempio:

 T const * const *p; 

significa che p è un puntatore al puntatore const per const T e tu leggi costantemente da destra a sinistra.

 const T * const *p; 

significa lo stesso, ma la consistenza è persa e devi ricordare che il const / volatile più a sinistra è legato a T solo e non a T *.

Se trovi questa discussione interessante, probabilmente troverai questo articolo di Dan Saks interessante. Non affronta direttamente la tua domanda, ma spiega perché preferisce

 VP const foo[]; 

a

 const VP foo[]; 

È perché dato

 typedef void *VP; 

potresti facilmente essere indotto a pensare che il secondo esempio sopra significhi

 const void *foo[]; // Wrong, actually: void *const foo[]; 

ma il primo esempio è più difficile da interpretare male.

Penso che sia una preferenza personale. Non c’è differenza tra le due varianti.

Essendo il codice è prevalentemente basato sull’inglese, i programmatori tendono a leggere da sinistra a destra, quindi const T& legge naturalmente dove il compilatore lo legge dentro e fuori da destra a sinistra, quindi T const& legge in modo naturale (riferimento a una const T)

Il mio ragionamento è il seguente:

Sembra stendere meglio la lingua se scrivi “const T &”, ma quando lo fai finisci con l’ambiguo “riferimento T costante”. Ho visto questo problema più di una volta nella comprensibilità del codice che ha permesso a qualcuno, anche a semi-esperto, di interpretare erroneamente cosa intendesse o come dichiarare un tipo più complesso.

Non riesco a pensare ad alcun esempio in questo momento, ma più di una volta ho risposto alle domande sulle dichiarazioni di tipo e sulla costanza in cui il problema era causato dall’abitudine di usare “const T &” invece di “T const &”. Anch’io scrivo in quel modo e quando sono diventato uno Sr. Developer, qualcuno che si occupa di mentoring e di creazione di standard di codice nei progetti, ho trovato molto più semplice per gli sviluppatori entry level quando costringo tutti a usare “T const &”. Suppongo che uno scomodo errore banale sarebbe perché questo codice viene compilato?

 const T* t = f(); t = 0; // assignment to const?? - no, it is not the T* that is const, just the T. 

Quando impari a leggerlo come fa il compilatore, diventa molto più facile capire cosa sia un dato tipo complesso e ti permette di dichiarare più facilmente tipi complessi quando ne hai bisogno. Per tipi complessi sto parlando di cose come:

T const * const &

Quando sai che il compilatore legge da destra a sinistra, dall’interno verso l’esterno, ciò che significa diventa piuttosto evidente e quando è necessario scriverne uno puoi farlo facilmente: riferimento al puntatore costante a una costante T. Ora scrivi la dichiarazione di un “riferimento a un puntatore a un puntatore costante a una T”. Se usi semplicemente la notazione da sinistra a destra, penso che lo troverai abbastanza facile.

In breve, anche se inizialmente sembra innaturale insegnare a usare la grammatica destra-> sinistra TUTTA la volta, invece che solo quando è richiesto (perché spesso lo è), troverai molto più facile ricordare cosa significa una riga di codice e come scrivere cosa intendi. È un po ‘come dire perché non autorizzo “,” nelle dichiarazioni:

 T* ptr1 = 0, ptr2 = 0; // oops!!! 

T* ptr1 = 0, ptr2 = 0; // oops!!!

// fai in questo modo per favore!
T * ptr1 = 0;
T * ptr2 = 0;

Tecnicamente è lo stesso, ma quando si cerca di far lavorare un sacco di persone con capacità diverse sulla stessa cosa, si tende ad assicurarsi che tutti utilizzino qualsiasi metodo sia il più facile da capire e da usare. La mia esperienza mi ha insegnato che “T const &” è quel metodo.

Questo perché alcuni trovano utile leggere la dichiarazione da destra a sinistra.

 char const* const char* 

sono entrambi puntatori a const char.

Ero un forte sostenitore di const T& perché legge logicamente da sinistra a destra (è un riferimento T costante ). (E probabilmente c’è qualche pregiudizio dal momento che la maggior parte del codice che ho incontrato a quel punto è stata scritta in questo modo.)

Nel corso degli anni ho riscontrato alcuni casi d’angolo (come più livelli di riferimento / puntatore, più dichiarazioni di variabili e puntatori di metodo) che mettono a dura prova le cose per i motivi per cui altre persone hanno già risposto. Spesso introdurre ulteriori typedef ti aiuta a “staccare” questi casi in una certa misura, ma poi devi trovare un nome per qualcosa anche se è usato una sola volta.

Il punto di svolta per me è stata l’introduzione auto in C ++ 11, specialmente se combinato con la gamma basata su. Con ciò, ho invertito e ora preferisco fortemente T const& (o “riferimento a T costante”, leggendo da destra a sinistra). Non solo è più coerente con il modo in cui il compilatore effettivamente analizza, significa che quando si sostituisce il tipo con auto , questo finisce sempre a sinistra, che a mio avviso si legge meglio.

Confrontare:

 for (const T& a : list) for (T& a : list) for (T const& a : list) for (T& a : list) for (const auto& a : list) for (auto& a : list) for (auto const& a : list) for (auto& a : list) 

Nota anche la colonna a destra, dove ho aggiunto la versione non costante dello stesso. Almeno per me, i cost contro vs. non-const e auto vs. espliciti sembrano tutti più consistenti nei casi in cui const appare dopo T.

Ma questa è una scelta di stile, e come tale non esiste una risposta assolutamente corretta.