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.