Ricerca…
Osservazioni
La parola chiave auto è un typename che rappresenta un tipo dedotto automaticamente.
Era già una parola chiave riservata in C ++ 98, ereditata da C. Nelle vecchie versioni di C ++, poteva essere usata per dichiarare esplicitamente che una variabile ha una durata di archiviazione automatica:
int main()
{
auto int i = 5; // removing auto has no effect
}
Quel vecchio significato è ora rimosso.
Campione automatico di base
La parola chiave auto fornisce l'auto-deduzione del tipo di una variabile.
È particolarmente utile quando si ha a che fare con nomi di caratteri lunghi:
std::map< std::string, std::shared_ptr< Widget > > table;
// C++98
std::map< std::string, std::shared_ptr< Widget > >::iterator i = table.find( "42" );
// C++11/14/17
auto j = table.find( "42" );
con loop basati su range :
vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
std::cout << n << ' ';
con lambda :
auto f = [](){ std::cout << "lambda\n"; };
f();
per evitare la ripetizione del tipo:
auto w = std::make_shared< Widget >();
per evitare copie sorprendenti e non necessarie:
auto myMap = std::map<int,float>();
myMap.emplace(1,3.14);
std::pair<int,float> const& firstPair2 = *myMap.begin(); // copy!
auto const& firstPair = *myMap.begin(); // no copy!
Il motivo della copia è che il tipo restituito è in realtà std::pair<const int,float> !
Modelli auto ed espressioni
auto può anche causare problemi quando entrano in gioco i modelli di espressione:
auto mult(int c) {
return c * std::valarray<int>{1};
}
auto v = mult(3);
std::cout << v[0]; // some value that could be, but almost certainly is not, 3.
Il motivo è che l' operator* su valarray fornisce un oggetto proxy che fa riferimento al valarray come mezzo di valutazione lazy. Usando l' auto , stai creando un riferimento ciondolante. Invece di mult aveva restituito uno std::valarray<int> , quindi il codice avrebbe sicuramente stampato 3.
auto, const e riferimenti
La parola chiave auto per sé rappresenta un tipo di valore, simile a int o char . Può essere modificato con la parola chiave const e il simbolo & per rappresentare rispettivamente un tipo const o un tipo di riferimento. Questi modificatori possono essere combinati.
In questo esempio, s è un tipo di valore (il suo tipo sarà dedotto come std::string ), quindi ogni iterazione del ciclo for copia una stringa dal vettore in s .
std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
std::cout << s << std::endl;
}
Se il corpo del loop modifica s (ad esempio chiamando s.append(" and stuff") ), solo questa copia verrà modificata, non il membro originale delle strings .
D'altra parte, se s è dichiarato con auto& sarà un tipo di riferimento (dedotto per essere std::string& ), così ad ogni iterazione del ciclo verrà assegnato un riferimento ad una stringa nel vettore:
for(auto& s : strings) {
std::cout << s << std::endl;
}
Nel corpo di questo ciclo, le modifiche a s influenzeranno direttamente l'elemento delle strings cui fa riferimento.
Infine, se s è dichiarato const auto& , sarà un tipo di riferimento const, nel senso che ad ogni iterazione del ciclo verrà assegnato un riferimento const a una stringa nel vettore:
for(const auto& s : strings) {
std::cout << s << std::endl;
}
All'interno del corpo di questo ciclo, s non può essere modificato (cioè non può essere chiamato alcun metodo non const).
Quando si utilizza l' auto con cicli basati for intervalli, è generalmente buona norma usare const auto& se il corpo del loop non modificherà la struttura in loop, poiché evita copie non necessarie.
Tipo di ritorno finale
auto viene utilizzato nella sintassi per il tipo di ritorno finale:
auto main() -> int {}
che è equivalente a
int main() {}
Molto utile combinato con decltype per usare i parametri invece di std::declval<T> :
template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
Lambda generico (C ++ 14)
C ++ 14 consente di utilizzare auto argomento auto in lambda
auto print = [](const auto& arg) { std::cout << arg << std::endl; };
print(42);
print("hello world");
Quella lambda è per lo più equivalente a
struct lambda {
template <typename T>
auto operator ()(const T& arg) const {
std::cout << arg << std::endl;
}
};
e poi
lambda print;
print(42);
print("hello world");
oggetti auto e proxy
A volte l' auto può comportarsi in modo non proprio come previsto da un programmatore. Il tipo deduce l'espressione, anche quando la deduzione del tipo non è la cosa giusta da fare.
Ad esempio, quando gli oggetti proxy sono utilizzati nel codice:
std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);
Qui flag sarebbe bool , ma std::vector<bool>::reference , poiché per la specializzazione bool del vector template l' operator [] restituisce un oggetto proxy con operator bool conversione definito.
Quando flags.push_back(true) modifica il contenitore, questo pseudo-riferimento potrebbe finire in sospeso, facendo riferimento a un elemento che non esiste più.
Rende anche possibile la prossima situazione:
void foo(bool b);
std::vector<bool> getFlags();
auto flag = getFlags()[5];
foo(flag);
Il vector viene scartato immediatamente, quindi flag è uno pseudo-riferimento a un elemento che è stato scartato. La chiamata a foo richiama un comportamento indefinito.
In casi come questo è possibile dichiarare una variabile con auto e inizializzarla eseguendo il casting sul tipo che si desidera dedurre:
auto flag = static_cast<bool>(getFlags()[5]);
ma a quel punto, semplicemente sostituire l' auto con il bool ha più senso.
Un altro caso in cui gli oggetti proxy possono causare problemi sono i modelli di espressione . In tal caso, i modelli a volte non sono progettati per durare oltre l'espressione completa corrente per motivi di efficienza e l'utilizzo dell'oggetto proxy sul prossimo causa un comportamento non definito.