Lascia scegliere il tipo al compilatore
Nella pagina precedente hai visto che ogni variabile C++ ha un tipo fisso: int, double, std::string e così via. Scrivere il tipo a mano va bene per int count = 0;, ma diventa rumoroso quando i tipi si allungano. La parola chiave auto (da C++11 in poi) permette al compilatore di dedurre il tipo dal valore che assegni, così scrivi il valore una volta e lasci che il compilatore completi il resto.
L'idea chiave: auto non è tipizzazione dinamica. Ogni variabile ha comunque un solo tipo concreto, fissato in fase di compilazione. auto ti risparmia solo di doverlo scrivere.
Perché auto vale la pena
Per tipi brevi e ovvi, auto count = 0; e int count = 0; sono ugualmente leggibili. Dove auto ripaga davvero è con i nomi di tipo lunghi e ripetitivi, quelli che ottieni dai contenitori e dagli iteratori della libreria standard.
Confronta la versione prolissa con quella con auto:
// Senza auto: in pratica il tipo si scrive due volte
std::vector<std::pair<std::string, int>>::iterator it = scores.begin();
// Con auto: il compilatore conosce già il tipo
auto it = scores.begin();
Entrambe dichiarano esattamente lo stesso tipo di iteratore. La seconda è più facile da leggere e non smetterà di essere corretta se in seguito cambi scores con un contenitore diverso.
Eccola in un programma completo:
auto nei cicli for basati su range
Il posto in cui incontrerai auto più spesso è il ciclo for basato su range. Non vorrai quasi mai scrivere a mano il tipo degli elementi, e il modo in cui scrivi auto decide se ottieni una copia o un riferimento.
Tre varianti che vedrai, e cosa significa ciascuna:
for (auto x : v):xè una copia di ogni elemento. Economica perint, uno spreco per oggetti grandi.for (auto& x : v):xè un riferimento; puoi modificare gli elementi sul posto.for (const auto& x : v):xè un riferimento di sola lettura. Usalo quando devi solo leggere.
Il prossimo programma modifica il contenitore tramite auto&:
Attenzione: se in quel ciclo scrivi for (auto n : nums) (senza &), n *= 10 cambierebbe in silenzio solo la copia, lasciando nums intatto. Il compilatore non ti avvisa: il ciclo semplicemente non fa nulla di utile.
Cosa elimina auto
Un auto semplice deduce il tipo nello stesso modo di un parametro di funzione passato per valore: elimina il const di primo livello, i riferimenti e volatile. Questo significa che auto ti dà sempre una copia nuova e modificabile, a meno che tu non chieda altrimenti.
Se vuoi mantenere const o evitare la copia, aggiungi tu i qualificatori. Lo schema è decorare auto come decoreresti qualsiasi altro tipo:
Quindi auto deduce il tipo di base; &, const e * sono manopole che aggiungi sopra. auto è il tipo, const auto& è un riferimento di sola lettura a quel tipo.
Errori comuni e insidie
auto ti risparmia fatica nello scrivere, ma non ti esonera dal capire i tipi. Alcune trappole colpiscono i principianti:
Devi inizializzare. Da una dichiarazione vuota auto non ha nulla da dedurre, quindi questo è un errore di compilazione vero e proprio:
auto x; // error: declaration of 'auto x' has no initializer
auto y = 0; // corretto
I letterali interi sono int, non double. auto half = 1 / 2; deduce int e memorizza 0, perché 1 / 2 è una divisione intera prima ancora che auto la veda. Il tipo segue il valore:
auto elimina il riferimento: attenzione alle copie a sorpresa. Se una funzione restituisce un riferimento e lo prendi con un semplice auto, ottieni una copia, e in un ciclo critico a volte questo è un vero problema di prestazioni (una copia profonda di un oggetto grande a ogni iterazione). Usa const auto& quando intendi "guarda, non prendere".
Non nascondere il tipo quando conta. auto result = compute(); va bene quando il tipo restituito da compute è ovvio dal contesto, ma se chi legge deve andare a cercare cosa sia davvero result, scrivere il tipo può essere la scelta più gentile. auto serve a ridurre il rumore, non a nascondere le intenzioni.
Prossimo: Costanti e const
Hai visto che auto elimina di proposito const a meno che tu non chieda di mantenerlo, il che solleva una domanda ovvia: cosa garantisce davvero const, e quando dovresti marcare un valore come immutabile? La prossima pagina approfondisce const, le espressioni costanti e perché "rendilo const di default" è una delle abitudini più utili in C++.
Domande frequenti
A cosa serve la parola chiave auto in C++?
auto dice al compilatore di dedurre il tipo della variabile dal suo inizializzatore. auto x = 5; rende x un int; auto y = 3.14; rende y un double. Il tipo viene fissato in fase di compilazione: auto non è tipizzazione dinamica, è una scorciatoia per non scrivere il tipo a mano.
auto mantiene const e i riferimenti in C++?
No. Un auto semplice elimina il const di primo livello, i riferimenti e volatile. Se l'origine è const int& r, allora auto x = r; produce una semplice copia int. Per mantenerli devi scriverli esplicitamente: usa const auto& per legare un riferimento di sola lettura senza copiare.
Si può dichiarare una variabile con auto senza inizializzarla?
No. auto x; è un errore di compilazione, perché non c'è alcun inizializzatore da cui il compilatore possa dedurre un tipo. Ogni variabile auto deve ricevere un valore nel punto in cui viene dichiarata.