Buscar..
Observaciones
La palabra clave auto
es un nombre de tipo que representa un tipo deducido automáticamente.
Ya era una palabra clave reservada en C ++ 98, heredada de C. En versiones anteriores de C ++, se podía usar para indicar explícitamente que una variable tiene una duración de almacenamiento automática:
int main()
{
auto int i = 5; // removing auto has no effect
}
Ese viejo significado ahora se ha eliminado.
Muestra auto básica
La palabra clave auto
proporciona la deducción automática del tipo de una variable.
Es especialmente conveniente cuando se trata de nombres tipográficos largos:
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 rango basado en bucles :
vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
std::cout << n << ' ';
con lambdas :
auto f = [](){ std::cout << "lambda\n"; };
f();
Para evitar la repetición del tipo:
auto w = std::make_shared< Widget >();
Para evitar copias sorprendentes e innecesarias:
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!
El motivo de la copia es que el tipo devuelto es en realidad std::pair<const int,float>
!
Plantillas de auto y expresión
auto
también puede causar problemas cuando las plantillas de expresión entran en juego:
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.
La razón es que el operator*
en valarray
le proporciona un objeto proxy que se refiere al valarray
como un medio de evaluación perezosa. Al usar auto
, estás creando una referencia que cuelga. En lugar de mult
ha devuelto un std::valarray<int>
, entonces el código definitivamente se imprimiría 3.
auto, const, y referencias
La palabra clave auto
por sí misma representa un tipo de valor, similar a int
o char
. Se puede modificar con la palabra clave const
y el símbolo &
para representar un tipo const o un tipo de referencia, respectivamente. Estos modificadores se pueden combinar.
En este ejemplo, s
es un tipo de valor (su tipo se deducirá como std::string
), por lo que cada iteración del bucle for
copia una cadena del vector en s
.
std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
std::cout << s << std::endl;
}
Si el cuerpo del bucle modifica s
(por ejemplo, al llamar a s.append(" and stuff")
), solo se modificará esta copia, no el miembro original de las strings
.
Por otro lado, si s
se declara con auto&
será un tipo de referencia (se inferirá que es std::string&
), por lo que en cada iteración del bucle se le asignará una referencia a una cadena en el vector:
for(auto& s : strings) {
std::cout << s << std::endl;
}
En el cuerpo de este bucle, las modificaciones a s
afectarán directamente el elemento de las strings
que hace referencia.
Finalmente, si s
se declara const auto&
, será un tipo de referencia const, lo que significa que en cada iteración del bucle se le asignará una referencia const a una cadena en el vector:
for(const auto& s : strings) {
std::cout << s << std::endl;
}
Dentro del cuerpo de este bucle, s
no se puede modificar (es decir, no hay métodos no const pueden ser llamados en él).
Cuando se utiliza el modo auto
con el rango for
bucles, generalmente es una buena práctica usar const auto&
si el cuerpo del bucle no modifica la estructura que se está repitiendo, ya que esto evita copias innecesarias.
Tipo de retorno final
auto
se utiliza en la sintaxis para el tipo de retorno final:
auto main() -> int {}
que es equivalente a
int main() {}
Mayormente útil combinado con decltype
para usar parámetros en lugar de std::declval<T>
:
template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
Lambda genérica (C ++ 14)
C ++ 14 permite usar auto
en argumento lambda
auto print = [](const auto& arg) { std::cout << arg << std::endl; };
print(42);
print("hello world");
Esa lambda es mayormente equivalente a
struct lambda {
template <typename T>
auto operator ()(const T& arg) const {
std::cout << arg << std::endl;
}
};
y entonces
lambda print;
print(42);
print("hello world");
objetos de auto y proxy
A veces, el auto
puede comportarse de la forma no esperada por un programador. El tipo deduce la expresión, incluso cuando la deducción de tipo no es lo correcto.
Como ejemplo, cuando se utilizan objetos proxy en el código:
std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);
Aquí la flag
no sería bool
, pero std::vector<bool>::reference
, ya que para la especialización bool
del vector
de plantilla el operator []
devuelve un objeto proxy con el operador de conversión operator bool
definido.
Cuando flags.push_back(true)
modifica el contenedor, esta pseudo-referencia podría terminar colgando, refiriéndose a un elemento que ya no existe.
También hace posible la siguiente situación:
void foo(bool b);
std::vector<bool> getFlags();
auto flag = getFlags()[5];
foo(flag);
El vector
se descarta inmediatamente, por lo que la flag
es una pseudo-referencia a un elemento que ha sido descartado. La llamada a foo
invoca un comportamiento indefinido.
En casos como este, puede declarar una variable con auto
e inicializarla mediante la conversión al tipo que desea deducir:
auto flag = static_cast<bool>(getFlags()[5]);
pero en ese punto, simplemente reemplazar el auto
por bool
tiene más sentido.
Otro caso donde los objetos proxy pueden causar problemas son las plantillas de expresión . En ese caso, las plantillas a veces no están diseñadas para durar más allá de la expresión completa actual por razones de eficiencia, y el uso del objeto proxy en la siguiente causa un comportamiento indefinido.