¿Debo usar la nueva función 'auto' de C ++ 11, especialmente en bucles?

20

¿Cuáles son las ventajas y desventajas de utilizar la autopalabra clave, especialmente en bucles for?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

Parece como si no sabe si tiene un iterador para un mapa o un vector no vas a saber si se debe utilizar firsto secondo sólo directamente propiedades de acceso del objeto, ¿no?

Esto me recuerda el debate de C # sobre si usar la palabra clave var. La impresión que tengo hasta ahora es que en el mundo de C ++ la gente está lista para adoptar la autopalabra clave con menos pelea que varen el mundo de C #. Para mí, mi primer instinto es que me gusta saber el tipo de variable para poder saber qué operaciones puedo realizar en ella.

Usuario
fuente
3
¡Espere! Hubo una pelea sobre si usar var? Me lo perdí.
pdr
21
Aún mejor, puede usar for (auto& it : x)(o sin referencia si desea copiar)
Tamás Szelei
77
Me parece que si estás escribiendo un bucle para iterar sobre el contenido xy ni siquiera sabes qué xes, no deberías escribir ese bucle en primer lugar ;-)
nikie
@fish: reglas de bucles for basados ​​en rango, pero habría sido pedante y hecho: 'for (T & it: x)' en su lugar cuando uso bucles for basados ​​en rango, ya que creo que usar auto es menos informativo. Una especie de mal uso del automóvil en mi mente.
Martiert
La pelea por usar var fue un poco tonta, especialmente en retrospectiva. Ver programación de culto Cargo .
Craig

Respuestas:

38

Las motivaciones en C ++ son más extremas, ya que los tipos pueden volverse mucho más intrincados y complejos que los tipos C # debido a la metaprogramación y otras cosas. autoes más rápido de escribir y leer y más flexible / mantenible que un tipo explícito. Quiero decir, ¿quieres empezar a escribir?

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

Eso ni siquiera es el tipo completo. Me perdí un par de argumentos de plantilla.

DeadMG
fuente
8
+1 para el ejemplo, pero eso también dice algo sobre el estado de C ++ "moderno".
zvrba
22
@zvrba: Sí, que las instalaciones genéricas son mucho más poderosas que las de C #.
DeadMG
44
para eso está typedef
gbjbaanb
19
@gbjbaanb No, para eso autoestá. Por diseño. typedefayuda, pero autoayuda más.
Konrad Rudolph
1
typedef invalida el argumento de que "no usar auto produce tipos realmente largos"
Michael
28

En tu ejemplo:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

Tiene que haber una declaración para xvisible. Por lo tanto, el tipo de itdebe ser obvio. Si el tipo de xno es obvio, entonces el método es demasiado largo o la clase es demasiado grande.

Kevin Cline
fuente
77
Además, xes un nombre de variable muy malo para un contenedor. En algunas situaciones, lo más probable es que solo mire el nombre (semánticamente valioso) e infiera las posibles operaciones.
Max
@Max: solo se usa xcomo un ejemplo genérico, tiendo a usar nombres de variables bastante descriptivos.
Usuario
@Usuario Por supuesto, no asumí que era un ejemplo del mundo real;)
Max
14

¡ Objeción ! Pregunta cargada.

¿Me puede explicar por qué el tercer código tiene ??, pero el primero y el segundo no? Para ser justos, su código debe leer lo siguiente:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

Allí. Mismo problema aunque no lo hayas usado auto.

Y en todos los casos, la respuesta es la misma: el contexto importa . No se puede hablar de manera significativa sobre un fragmento de código de forma aislada. Incluso si no hubiera utilizado plantillas pero algún tipo concreto, esto solo habría trasladado el problema a otro lugar, ya que el lector de su código tendría que saber sobre la declaración de dicho tipo.

Si el uso autoen esas situaciones hace que su código sea ilegible, debe tratar esto como una señal de advertencia de que hay algo mal con el diseño de su código. Por supuesto, hay casos en los que los detalles de bajo nivel son importantes (como cuando se trata de operaciones de bits o API heredadas) en los que un tipo explícito podría ayudar a la legibilidad. Pero en general, no.

En cuanto a var(ya que lo mencionaste explícitamente), también hay un gran consenso en la comunidad de C # para usar var. Los argumentos en contra de su uso generalmente se basan en falacias .

Konrad Rudolph
fuente
1
Creo que el punto era que con auto no sabes lo que pones a continuación ... ¿es tu código "algo" específico o es un desempaque relacionado con el tipo de datos para llegar a tu objeto de datos que tiene el método "algo"
Michael Shaw
1
@Ptolemy Y mi punto es: en los otros dos códigos que también no sabe (por lo general) que poner al lado: Tes tan opaca al usuario como auto. Sin embargo, se supone que uno está bien y el otro no. Eso no tiene sentido. En el caso de OP, Tes un sustituto de un tipo arbitrario. En código real, puede ser el uso de plantillas (for typename std::vector<T>::iterator…)o una interfaz de clase. En ambos casos, el tipo real está oculto para el usuario y, sin embargo, habitualmente escribimos dicho código sin problemas.
Konrad Rudolph el
1
En realidad, sí lo haces. Si es un vector, sabe que debe hacer -> y luego tiene acceso a su tipo de datos. Si es un mapa, sabe que debe hacer -> segundo-> y luego tiene acceso a su tipo de datos, si es automático, no sabe qué debe hacer para acceder a su tipo de datos. Parece confundir el "cuál es el tipo de datos contenido en la colección STL" con "qué tipo de colección STL tenemos". auto empeora este problema.
Michael Shaw
1
@Ptolomeo Todos estos argumentos son tan verdaderos cuando se usa auto. Es trivial ver qué operaciones xadmite el contexto. De hecho, el tipo no le proporciona información adicional: en cualquier caso necesita algo secundario (IDE, documentación, conocimiento / memoria) para indicarle el conjunto de operaciones compatibles.
Konrad Rudolph el
1
@Ptolomeo Eso solo es cierto si se encuentra en una situación muy complicada en la que no sabe qué beginretorna pero sabe qué std::vector<>::iteratores. Y debe utilizar una herramienta de programación incorrecta que no pueda proporcionarle esta información de manera trivial. Esto es muy complicado. En realidad, ya sea que sabe tanto beginy iteratoro ninguno, y que usted debe utilizar un IDE o editor que fácilmente puede hacer que la información relevante disponible para usted. Todo IDE moderno y editor de programación puede hacer eso.
Konrad Rudolph el
11

PRO

Tu codigo :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

no se va a compilar, debido al nombre dependiente de la plantilla.

Esta es la sintaxis correcta:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Ahora mire cuánto dura la declaración de tipo. Esto explica por qué autose introdujo la palabra clave. Esta :

for( auto it = x.begin(); it != x.end(); i++)

Es más conciso. Entonces, esto es un profesional.


ESTAFA

Tienes que tener un poco de cuidado. Con la palabra clave auto, obtienes el tipo que declaraste.

Por ejemplo :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

vs

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

Para concluir: sí, debería hacerlo, pero no usarlo en exceso. Algunas personas tienden a usarlo demasiado y a poner el auto en todas partes, como en el siguiente ejemplo:

auto i = 0;

vs

int i = 0;
BЈовић
fuente
auto i = 0. Culpable. Yo hago eso. Pero eso es porque sé que 0es un literal de tipo int. (y una constante octal ;-))
Laurent LA RIZZA
6

¡Si deberías! autono borra el tipo; incluso si "no sabe" qué x.begin()es, el compilador lo sabe e informará de un error si intenta utilizar el tipo incorrectamente. Además, no es inusual emular mapcon a vector<pair<Key,Value>>, por lo que el uso del código autofuncionará para ambas representaciones de diccionario.

zvrba
fuente
4

Sí, debe usar autocomo regla predeterminada. Tiene ventajas crudas sobre especificar explícitamente el tipo:

  • No te hace escribir cosas que el compilador ya conoce.
  • Hace que el tipo de variables "siga" cualquier cambio en los tipos de valores de retorno.
  • Al hacerlo, evita la introducción silenciosa de conversiones implícitas y el corte en la inicialización de variables locales.
  • Elimina la necesidad de algunos cálculos de tipo explícito en las plantillas.
  • Elimina la necesidad de nombrar tipos de retorno con nombres largos. (los que copia y pega del diagnóstico del compilador)

Aquí es donde puedes elegir. También hay casos en los que no tienes otra opción:

  • Permite la declaración de variables de tipos indescriptibles, como el tipo de una lambda.

Siempre que sepa exactamente lo que autohace, no tiene inconvenientes.

Laurent LA RIZZA
fuente