Pregunta La nueva palabra clave "auto"; ¿Cuándo debería usarse para declarar un tipo de variable? [duplicar]


Posible duplicado:
¿Cuánto es demasiado con la palabra clave auto C ++ 0x 

¿Tenemos (como comunidad) suficiente experiencia para determinar cuándo y / o si se está abusando del automóvil?

Lo que realmente estoy buscando es una guía de mejores prácticas en

  • cuando usar auto
  • cuando debe ser evitado

Reglas simples que pueden seguirse rápidamente en el 80% de los casos.

Como contexto, esta pregunta es provocada por mi respuesta aquí


76
2017-08-01 15:09


origen


Respuestas:


Creo que cuando el tipo es muy conocido entre los co-programadores que trabajan (o trabajarían) en su proyecto, entonces auto se puede utilizar, como en el siguiente código:

//good : auto increases readability here
for(auto it = v.begin(); it != v.end(); ++it) //v is some [std] container
{
      //..
}

O, más en general,

//good : auto increases readability here
for(auto it = std::begin(v); it != std::end(v); ++it)//v could be array as well
{
      //..
}

Pero cuando el tipo no es muy conocido y se usa con poca frecuencia, entonces pienso auto parece reducir la legibilidad, como aquí:

//bad : auto decreases readability here
auto obj = ProcessData(someVariables);

Mientras que en el primer caso, el uso de auto parece muy bueno y no reduce la legibilidad, y por lo tanto, se puede usar ampliamente, pero en el último caso, reduce la legibilidad y, por lo tanto, no debe utilizarse.


Otro lugar donde auto se puede utilizar es cuando usted usa new1 o make_* funciones, como aquí:

//without auto. Not that good, looks cumbersome
SomeType<OtherType>::SomeOtherType * obj1 = new SomeType<OtherType>::SomeOtherType();
std::shared_ptr<XyzType> obj2 = std::make_shared<XyzType>(args...);
std::unique_ptr<XyzType> obj2 = std::make_unique<XyzType>(args...);

//With auto. good : auto increases readability here
auto obj1 = new SomeType<OtherType>::SomeOtherType();
auto obj2 = std::make_shared<XyzType>(args...);
auto obj3 = std::make_unique<XyzType>(args...);

Aquí es muy bueno, ya que reduce el uso del teclado, sin reducir la legibilidad, ya que cualquiera puede saber el tipo de objetos siendo creado, solo mirando el código.

1. Evite usar new y sin embargo, indicadores crudos.


En algún momento, el tipo es tan irrelevante que el conocimiento del tipo ni siquiera es necesario, como en plantilla de expresión; de hecho, prácticamente es imposible escribir el tipo (correctamente), en tales casos auto es un alivio para los programadores. He escrito una biblioteca de plantillas de expresiones que se puede usar como:

foam::composition::expression<int> x;

auto s = x * x;       //square
auto c = x * x * x;   //cube
for(int i = 0; i < 5 ; i++ )
    std::cout << s(i) << ", " << c(i) << std::endl; 

Salida:

0, 0
1, 1
4, 8
9, 27
16, 64

Ahora compare el código anterior con el siguiente equivalente código que no usa auto:

foam::composition::expression<int> x;

//scroll horizontally to see the complete type!!
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply>> s = x * x; //square
foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply> >, foam::composition::expression<int>, foam::operators::multiply>> c = x * x * x; //cube

for(int i = 0; i < 5 ; i++ )
    std::cout << s(i) << ", " << c(i) << std::endl; 

Como puedes ver, en tales casos auto hace tu vida exponencialmente más fácil. Las expresiones utilizadas anteriormente son muy simples; pensar en el tipo de expresiones más complejas:

auto a = x * x - 4 * x + 4; 
auto b = x * (x + 10) / ( x * x+ 12 );
auto c = (x ^ 4 + x ^ 3 + x ^ 2 + x + 100 ) / ( x ^ 2 + 10 );

El tipo de tales expresiones sería aún más grande y feo, pero gracias a auto, ahora podemos permitir que el compilador infiera el tipo de expresiones.


Entonces, la línea de fondo es: la palabra clave auto podría incrementar o disminuir claridad y legibilidad de su código, dependiendo del contexto. Si el contexto deja en claro qué tipo es, o al menos, cómo debe usarse (en el caso de un iterador de contenedor estándar) o el conocimiento del tipo real ni siquiera es necesario (como en las plantillas de expresión), luego auto debiera ser usado, y si el contexto no lo deja claro y no es muy común (como el segundo caso anterior), entonces debería ser mejor evitado.


127
2017-08-01 15:13



Fácil. Úselo cuando no me importaque tipo es Por ejemplo

for (auto i : some_container) {
   ...

Todo lo que me importa aquí es que i es lo que sea que esté en el contenedor.

Es un poco como typedefs.

typedef float Height;
typedef double Weight;
//....
Height h;
Weight w;

Aquí, no me importa si h y w son flotadores o dobles, solo que son cualquier tipo es adecuado para expresar alturas y pesos.

O considera

for (auto i = some_container .begin (); ...

Aquí lo único que me importa es que es un iterador adecuado, compatible operator++(), es algo así como pato escribiendo a este respecto.

Además, el tipo de lambdas no se puede escribir, por lo auto f = []... es un buen estilo La alternativa es lanzar a std::function pero eso viene con gastos generales.

Realmente no puedo concebir un "abuso" de auto. Lo más que puedo imaginar es privarme de una conversión explícita a un tipo significativo, pero no usaría auto para eso, construirías un objeto del tipo deseado.

Si tu poder eliminar un poco de redundancia en su código sin introducir efectos secundarios, entonces debe se bueno para hacerlo


14
2017-08-01 15:27



Aplicaría la misma regla que para var Cª#: úsalo generosamente. Eso aumenta legibilidad. A menos que el tipo de una variable sea realmente lo suficientemente importante como para establecerse explícitamente, en qué casos esto debería hacerse (duh).

Aún así, sostengo que (especialmente en lenguajes tipados estáticamente) el compilador es mucho mejor en rastrear tipos para nosotros que nosotros. La mayoría de las veces, el exacto el tipo no es terriblemente importante de todos modos (de lo contrario, las interfaces no funcionarían en la práctica). Es más importante saber qué operaciones están permitidas. El contexto debería decirnos eso.

Además, auto puede en realidad prevenir errores, evitando conversiones implícitas no deseadas en las inicializaciones. En general, la declaración Foo x = y; realizará una conversión implícita si y no es de tipo Foo y existe una conversión implícita. Esta es la razón para evitar tener conversiones implícitas en primer lugar. Desafortunadamente, C ++ ya tiene muchos de ellos.

Escritura auto x = y; evitará este problema en principio.

Por otro lado, debe quedar claro que cuando realizo cálculos que asumen tal o cual número de bytes en un entero, el tipo explícito de la variable debe conocerse y debe indicarse claramente.

No todos los casos son tan claros, pero sostengo que la mayoría son, y eso

  1. en la mayoría de los casos es fácil ver si se necesita conocer un tipo explícito, y
  2. la necesidad de tipos explícitos es comparativamente rara.

Eric Lippert, desarrollador principal en el equipo compilador de C #, ha declarado más o menos lo mismo con respecto a var.


10
2017-08-01 15:28



Creo que la respuesta a tu primera pregunta es algo así como no. Sabemos lo suficiente como para armar algunas pautas sobre cuándo usar o evitar auto, pero todavía dejan bastantes casos en los que lo mejor que podemos decir actualmente es que todavía no podemos dar mucho en el camino de un consejo objetivo sobre ellos.

El caso obvio en el que casi tener para usarlo está en una plantilla cuando quiere (por ejemplo) el tipo apropiado para mantener el resultado de alguna operación en dos parámetros genéricos. En un caso como este, la única posibilidad de abuso no sería realmente el abuso de auto sí mismo, pero si el tipo general de operación que está haciendo (o el tipo de plantilla que está escribiendo, etc.) es algo que sería mejor evitar.

También hay al menos algunas situaciones en las que claramente debe evitar auto. Si está utilizando algo así como un tipo de proxy en el que depende de la conversión de proxy-> target para hacer parte del trabajo en cuestión, auto intentará (intentará) crear un objetivo del mismo tipo que la fuente para que la conversión no suceda. En algunos casos, eso puede retrasar la conversión, pero en otros no funcionará en absoluto (por ejemplo, si el tipo de proxy no es compatible con la asignación, que a menudo es el caso).

Otro ejemplo sería cuando necesita asegurarse de que una variable en particular tenga un tipo específico por el bien de algo así como una interfaz externa. Solo por ejemplo, considere aplicar la máscara de red a una dirección IP (v4). En aras de la argumentación, supongamos que está trabajando con los octetos individuales de la dirección (por ejemplo, representando cada uno como unsigned char), así que terminamos con algo así como octets[0] & mask[0]. Gracias a las reglas de promoción tipo C, incluso si ambos operandos son unsigned chars, el resultado generalmente será int. Nosotros necesitar el resultado es ser un unsigned char sin embargo (es decir, un octeto) no un int (por lo general 4 octetos) sin embargo. Como tal, en esta situación, auto casi con toda seguridad sería inapropiado.

Eso aún deja muchos casos en los que es una decisión de juicio. Mío tendencia para estos casos es para tratar auto como el predeterminado, y solo use un tipo explícito en casos que son al menos un poco como el último caso que he citado anteriormente, incluso si un tipo particular no es necesario para una operación correcta que realmente querer un tipo particular, incluso si eso pudiera implicar una conversión implícita.

Mi suposición (pero es solo una suposición) es que con el tiempo, voy a probablemente tender aún más en esa dirección. A medida que me vaya acostumbrando al compilador escogiendo los tipos, encontraré que hay un buen número de casos en los que actualmente pensar Debo especificar el tipo, realmente no necesito y el código estará bien.

Sospecho que muchos de nosotros (y los mayores / más experimentados somos, probablemente mientras peor lo hagamos) utilizarán tipos explícitos por razones que en última instancia se remontan a cierto sentimiento sobre el rendimiento, y creen que nuestra elección mejorará el rendimiento . Parte del tiempo que mayo incluso estar en lo correcto, pero como la mayoría de nosotros con esa experiencia hemos descubierto, nuestras suposiciones a menudo son erróneas (especialmente cuando se basan en suposiciones implícitas), y los compiladores y procesadores generalmente también mejoran en tales cosas con el tiempo.


4
2017-08-01 16:52



He usado idiomas con inferencia de tipo completo. No veo ninguna razón para no poner auto en todas partes es técnicamente posible *. De hecho, es posible que ya haya escrito auto i = 0;, dónde int es un personaje más corto que auto. Ni siquiera estoy seguro de haberlo hecho, porque el fondo es: no me importa el tipado manifiesto.

*: por ejemplo auto int[] = { 0, 1, 2, 3 } no funciona


2
2017-08-01 15:52



Úselo solo con tipos repetitivos largos, como plantillas largas y tipos de funciones lambda. Intenta evitarlo si puedes aclarar las cosas.


2
2017-08-01 15:14