¿El auto hace que el código C ++ sea más difícil de entender?


122

Vi una conferencia de Herb Sutter donde anima a todos los programadores de C ++ a usar auto.

Tuve que leer el código C # hace algún tiempo, donde varse usaba ampliamente y el código era muy difícil de entender; cada vez que varse usaba tenía que verificar el tipo de retorno del lado derecho. A veces más de una vez, ¡porque olvidé el tipo de variable después de un tiempo!

Sé que el compilador conoce el tipo y no tengo que escribirlo, pero es ampliamente aceptado que debemos escribir código para programadores, no para compiladores.

También sé que es más fácil de escribir:

auto x = GetX();

Que:

someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();

Pero esto se escribe solo una vez y el GetX()tipo de retorno se verifica muchas veces para comprender qué tipo xtiene.

Esto me hizo preguntarme: ¿hace autoque el código C ++ sea más difícil de entender?


29
¿Realmente necesita verificar el tipo de devolución cada vez ? ¿Por qué no está claro el tipo del código? autoa menudo hace que las cosas sean más difíciles de leer cuando ya son difíciles de leer, es decir, funciones demasiado largas, variables mal nombradas, etc.
R. Martinho Fernandes

25
El "arte" de usar autoes muy parecido a determinar cuándo usar typedef. Depende de usted determinar cuándo dificulta y cuándo ayuda.
ahenderson

18
Pensé que tenía el mismo problema, pero luego me di cuenta de que solo podía entender el código sin conocer los tipos. por ejemplo: "auto idx = get_index ();" entonces idx es algo que tiene un índice. Cuál es el tipo exacto, es bastante irrelevante para la mayoría de los casos.
PlasmaHH

31
Así que no escriba auto x = GetX();, elija un nombre mejor que el xque realmente le dice qué hace en ese contexto específico ... de todos modos, a menudo es más útil que su tipo.
Jonathan Wakely

11
Si el uso de más inferencia de tipos dificulta que un programador lea el código, ya sea el código o el programador necesita una mejora seria.
CA McCann

Respuestas:


100

Respuesta corta: Más completamente, mi opinión actual autoes que debe usar autopor defecto a menos que desee explícitamente una conversión. (Un poco más precisamente, "... a menos que desee comprometerse explícitamente con un tipo, que casi siempre se debe a que desea una conversión").

Respuesta más larga y justificación:

Escriba un tipo explícito (en lugar de auto) solo cuando realmente desee comprometerse explícitamente con un tipo, lo que casi siempre significa que desea obtener explícitamente una conversión a ese tipo. De la parte superior de mi cabeza, recuerdo dos casos principales:

  • (Común) La initializer_listsorpresa que auto x = { 1 };deduce initializer_list. Si no quiere initializer_list, diga el tipo, es decir, solicite explícitamente una conversión.
  • (Raro) El caso de las plantillas de expresión, como el que auto x = matrix1 * matrix 2 + matrix3;captura un tipo auxiliar o proxy que no debe ser visible para el programador. En muchos casos, está bien y es benigno capturar ese tipo, pero a veces si realmente desea que se colapse y haga el cálculo, diga el tipo, es decir, solicite nuevamente una conversión explícitamente.

De manera rutinaria, use autode manera predeterminada, de lo contrario, porque el uso autoevita las trampas y hace que su código sea más correcto, más sostenible y robusto y más eficiente. Aproximadamente en orden de mayor a menor importancia, en el espíritu de "escribir para mayor claridad y corrección primero":

  • Corrección: Usando autogarantías obtendrá el tipo correcto. Como dice el refrán, si se repite (diga el tipo de forma redundante), puede mentir y mentirá (equivocarse). Aquí hay un ejemplo habitual: void f( const vector<int>& v ) { for( /*…*en este punto, si escribe explícitamente el tipo de iterador, desea recordar escribir const_iterator(¿lo hizo?), Mientras que autolo hace bien.
  • Mantenibilidad y robustez: el uso autohace que su código sea más robusto frente al cambio, porque cuando el tipo de expresión cambia, autocontinuará resolviéndose en el tipo correcto. Si, en cambio, se compromete con un tipo explícito, al cambiar el tipo de la expresión se inyectarán conversiones silenciosas cuando el nuevo tipo se convierta en el tipo anterior, o las compilaciones innecesarias se rompan cuando el nuevo tipo siga funcionando, como el tipo anterior pero no se convierta al antiguo tipo (por ejemplo, cuando cambia un mapa un unordered_map, lo cual siempre está bien si no confía en el orden, usando autopara sus iteradores cambiará sin problemas de map<>::iteratora unordered_map<>::iterator, pero usandomap<>::iterator en todas partes significa explícitamente que estarás perdiendo tu valioso tiempo en una onda de corrección de código mecánico, a menos que un pasante esté caminando y puedas evitar el trabajo aburrido sobre ellos).
  • Rendimiento: Debido autogarantías ninguna conversión implícita sucederán, garantiza un mejor rendimiento por defecto. Si, por el contrario, dice el tipo y requiere una conversión, a menudo obtendrá una conversión silenciosa, lo haya esperado o no.
  • Facilidad de uso : el uso autoes su única buena opción para tipos difíciles de deletrear e indescifrables, como lambdas y ayudantes de plantilla, sin recurrir a decltypeexpresiones repetitivas o indirecciones menos eficientes std::function.
  • Conveniencia: Y sí, autoes menos escribir. Menciono eso último para completar porque es una razón común para que te guste, pero no es la razón más importante para usarlo.

Por lo tanto: Prefiero decir autopor defecto. Ofrece tanta simplicidad y rendimiento y bondad de claridad que solo te estás lastimando a ti mismo (y a los futuros mantenedores de tu código) si no lo haces. Solo comprométase con un tipo explícito cuando realmente lo diga, lo que casi siempre significa que desea una conversión explícita.

Sí, hay (ahora) un GotW sobre esto.


14
Me parece útil incluso cuando quiero una conversión. Me permite pedir explícitamente una conversión sin repetir el tipo: auto x = static_cast<X>(y). Esto static_castdeja en claro que la conversión es a propósito y evita las advertencias del compilador sobre la conversión. Normalmente, evitar las advertencias del compilador no es tan bueno, pero estoy de acuerdo con no recibir una advertencia sobre una conversión que consideré cuidadosamente cuando escribí static_cast. Aunque no haría esto si no hay advertencias ahora, pero quiero recibir advertencias en el futuro si los tipos cambian de una manera potencialmente peligrosa.
Bjarke Hammersholt Roune

66
Una cosa que encuentro autoes que debemos esforzarnos por programar contra interfaces (no en el sentido OOP), no contra implementaciones específicas. Es lo mismo con las plantillas, de verdad. ¿Se queja de "código difícil de leer" porque tiene un parámetro de tipo de plantilla Tque se usa en todas partes? No, no lo creo. En las plantillas también codificamos contra una interfaz, lo que mucha gente lo llama escribir en tiempo de compilación.
Xeo

66
"Al utilizar las garantías automáticas, obtendrá el tipo correcto". No es cierto del todo. Solo garantiza que obtendrá el tipo prescrito por alguna otra parte de su código. Si eso es correcto o no, no está del todo claro cuando lo ocultas detrás auto.
Carreras de ligereza en órbita

Estoy realmente sorprendido de que a nadie le importen los IDE ... Incluso los IDE modernos no admiten correctamente saltar a la definición de clase / estructura en caso de autovariable, pero casi todos lo hacen correctamente con una especificación de tipo explícita. Nadie usa IDE? ¿Todos usan solo variables int / float / bool? ¿Todos prefieren documentación externa para bibliotecas en lugar de encabezados auto documentados?
avtomaton

that GotW: herbsutter.com/2013/08/12/… No veo cómo esa "sorpresa initiallist_list " es una sorpresa; los corchetes alrededor de =RHS no tienen mucho sentido por ninguna otra interpretación (lista de inicio con barra, pero necesita saber lo que está inicializando, que es un oxímoron auto). Lo que es sorprendente auto i{1}también es deducir initializer_list, a pesar de implicar que no tome esta lista de inicialización arriostrada, sino que tome esta expresión y use su tipo ... pero también llegamos initializer_listallí. Afortunadamente, C ++ 17 soluciona todo esto bien.
underscore_d

112

Es una situación caso por caso.

A veces hace que el código sea más difícil de entender, a veces no. Tomemos, por ejemplo:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

es definitivamente fácil de entender y definitivamente más fácil de escribir que la declaración real del iterador.

He estado usando C ++ por un tiempo ahora, pero puedo garantizar que obtendré un error de compilación en mi primer intento porque me olvidaría del const_iteratore inicialmente iría por el iterator... :)

Lo usaría para casos como este, pero no donde realmente ofusca el tipo (como su situación), pero esto es puramente subjetivo.


45
Exactamente. A quién diablos le importa el tipo. Es un iterador. No me importa el tipo, todo lo que necesito saber es que puedo usarlo para iterar.
R. Martinho Fernandes

55
+1. Incluso si nombraste el tipo, lo nombrarías así std::map<int, std::string>::const_iterator, así que no es como si el nombre te dijera mucho sobre el tipo de todos modos.
Steve Jessop

44
@SteveJessop: Al menos me dice dos cosas: la clave es inty el valor es std::string. :)
Nawaz

16
@Nawaz: y eso no se puede asignar it->secondya que es un iterador constante. Todo lo cual la información es una repetición de lo que está en la línea anterior, const std::map<int, std::string>& x. Decir cosas varias veces en ocasiones informa mejor, pero de ninguna manera es una regla general :-)
Steve Jessop

11
TBH Preferiría for (anX : x)hacerlo aún más obvio que solo estamos iterando x. El caso normal donde necesita un iterador es cuando está modificando el contenedor, pero xesconst&
MSalters

94

Míralo de otra manera. Escribes:

std::cout << (foo() + bar()) << "\n";

o:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

A veces no ayuda deletrear el tipo explícitamente.

La decisión de si necesita mencionar el tipo no es la misma que la decisión de si desea dividir el código en varias declaraciones definiendo variables intermedias. En C ++ 03 los dos estaban vinculados, se puede considerar autocomo una forma de separarlos.

A veces, hacer explícitos los tipos puede ser útil:

// seems legit    
if (foo() < bar()) { ... }

vs.

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

En los casos en que declara una variable, el uso autodeja que el tipo no se exprese tal como lo es en muchas expresiones. Probablemente deberías intentar decidir por ti mismo cuándo eso ayuda a la legibilidad y cuándo dificulta.

Puede argumentar que mezclar tipos con y sin signo es un error para empezar (de hecho, algunos argumentan además que no se deben usar tipos sin signo). La razón por la que es posiblemente un error es que hace que los tipos de operandos sean de vital importancia debido al comportamiento diferente. Si es malo saber los tipos de sus valores, entonces probablemente tampoco sea malo no tener que conocerlos. Entonces, siempre que el código no sea confuso por otras razones, eso lo hace autocorrecto, ¿verdad? ;-)

Particularmente al escribir código genérico, hay casos en los que el tipo real de una variable no debería ser importante, lo que importa es que satisfaga la interfaz requerida. Por lo tanto, autoproporciona un nivel de abstracción en el que ignora el tipo (pero, por supuesto, el compilador no lo hace, lo sabe). Trabajar en un nivel adecuado de abstracción puede ayudar bastante a la legibilidad, trabajar en el nivel "incorrecto" hace que leer el código sea un trabajo pesado.


21
+1 le autopermite crear variables con nombre con tipos sin nombre o sin interés. Los nombres significativos pueden ser útiles.
Mankarse

Mezcla con signo y sin signo si usa unsigned para el uso adecuado: aritmética modular. No lo es si usa indebidamente sin firmar para un entero positivo. Casi ningún programa tiene un uso para unsigned, pero el lenguaje central obliga a su definición inane de sizeofcomo unsigned en usted.
curioso

27

OMI, estás viendo esto más o menos a la inversa.

No se trata de autollevar a un código ilegible o incluso menos legible. Se trata de (tener la esperanza de que) tener un tipo explícito para el valor de retorno compensará el hecho de que (aparentemente) no está claro qué tipo sería devuelto por alguna función en particular.

Al menos en mi opinión, si tiene una función cuyo tipo de retorno no es inmediatamente obvio, ese es su problema allí mismo. Lo que hace la función debería ser obvio por su nombre, y el tipo del valor de retorno debería ser obvio por lo que hace. Si no, esa es la verdadera fuente del problema.

Si hay un problema aquí, no es con auto. Es con el resto del código, y las posibilidades son bastante buenas de que el tipo explícito sea suficiente como una curita para evitar que vea y / o solucione el problema central. Una vez que haya solucionado ese problema real, la legibilidad del código usando autogeneralmente estará bien.

Supongo que, para ser justos, debería agregar: he tratado algunos casos en los que tales cosas no eran tan obvias como quisiera, y solucionar el problema también era bastante insostenible. Solo por un ejemplo, hice un poco de consultoría para una compañía hace un par de años que previamente se había fusionado con otra compañía. Terminaron con una base de código que estaba más "unida" que realmente fusionada. Los programas constitutivos habían comenzado a usar bibliotecas diferentes (pero bastante similares) para propósitos similares, y aunque estaban trabajando para fusionar las cosas de manera más limpia, aún lo hacían. En un buen número de casos, la única forma de adivinar qué tipo devolvería una función determinada era saber dónde se había originado esa función.

Incluso en ese caso, puede ayudar a aclarar algunas cosas. En ese caso, todo el código comenzó en el espacio de nombres global. El simple hecho de mover una cantidad considerable en algunos espacios de nombres eliminó los conflictos de nombres y facilitó bastante el seguimiento de tipos.


17

Hay varias razones por las que no me gusta el auto para uso general:

  1. Puede refactorizar el código sin modificarlo. Sí, esta es una de las cosas que a menudo se enumeran como un beneficio del uso de automóviles. Simplemente cambie el tipo de retorno de una función, y si todo el código que lo llama usa auto, ¡no se requiere ningún esfuerzo adicional! Tocas la compilación, se construye (0 advertencias, 0 errores) y simplemente continúas e ingresas tu código sin tener que lidiar con el desorden de mirar y modificar potencialmente los 80 lugares donde se usa la función.

Pero espera, ¿es realmente una buena idea? ¿Qué pasa si el tipo importaba en media docena de esos casos de uso, y ahora ese código realmente se comporta de manera diferente? Esto también puede romper implícitamente la encapsulación, modificando no solo los valores de entrada, sino también el comportamiento de la implementación privada de otras clases que llaman a la función.

1a. Creo en el concepto de "código autodocumentado". El razonamiento detrás del código autodocumentado es que los comentarios tienden a estar desactualizados, ya no reflejan lo que está haciendo el código, mientras que el código en sí mismo, si se escribe de manera explícita, se explica por sí mismo, siempre se mantiene actualizado. en su intención, y no te dejará confundido con comentarios obsoletos. Sin embargo, si los tipos se pueden cambiar sin necesidad de modificar el código en sí, entonces el código / las variables en sí pueden volverse obsoletos. Por ejemplo:

auto bThreadOK = CheckThreadHealth ();

Excepto que el problema es que CheckThreadHealth () en algún momento fue refactorizado para devolver un valor de enumeración que indica el estado del error, si lo hay, en lugar de un bool. Pero la persona que realizó ese cambio no inspeccionó esta línea de código en particular, y el compilador no fue de ayuda porque compiló sin advertencias o errores.

  1. Es posible que nunca sepas cuáles son los tipos reales. Esto también suele figurar como un "beneficio" principal del automóvil. ¿Por qué aprender lo que te está dando una función, cuando puedes decir: "¿A quién le importa? ¡Se compila!"

Incluso funciona, probablemente. Digo que funciona, porque a pesar de que está haciendo una copia de una estructura de 500 bytes para cada iteración de bucle, por lo que puede inspeccionar un solo valor, el código sigue siendo completamente funcional. Entonces, incluso las pruebas de su unidad no lo ayudan a darse cuenta de que el código incorrecto se esconde detrás de ese auto simple e inocente. La mayoría de las personas que escanean el archivo tampoco lo notarán a primera vista.

Esto también puede empeorar si no sabe cuál es el tipo, pero elige un nombre de variable que hace una suposición errónea sobre lo que es, en realidad logra el mismo resultado que en 1a, pero desde el principio en lugar de post-refactor.

  1. Escribir el código cuando se escribe inicialmente no es la parte de programación que requiere más tiempo. Sí, auto hace que escribir código sea más rápido inicialmente. Como descargo de responsabilidad, escribo> 100 WPM, por lo que tal vez no me moleste tanto como a los demás. Pero si todo lo que tuviera que hacer fuera escribir un código nuevo todo el día, sería un campista feliz. La parte de la programación que consume más tiempo es diagnosticar errores de borde de borde difíciles de reproducir en el código, que a menudo resultan de problemas sutiles y no obvios, como el uso excesivo de auto (referencia frente a copia, con y sin signo, flotante con int, bool con puntero, etc.).

Me parece obvio que auto se introdujo principalmente como una solución alternativa para una sintaxis terrible con tipos de plantillas de biblioteca estándar. En lugar de tratar de corregir la sintaxis de la plantilla con la que las personas ya están familiarizadas, lo que también puede ser casi imposible de hacer debido a todo el código existente que podría romper, agregue una palabra clave que básicamente oculte el problema. Esencialmente, lo que podríamos llamar un "hack".

En realidad no tengo ningún desacuerdo con el uso de auto con contenedores de biblioteca estándar. Obviamente es para lo que se creó la palabra clave, y es probable que las funciones en la biblioteca estándar no cambien fundamentalmente su propósito (o tipo para el caso), lo que hace que el auto sea relativamente seguro de usar. Pero sería muy cauteloso al usarlo con su propio código e interfaces que pueden ser mucho más volátiles y potencialmente sujetos a cambios más fundamentales.

Otra aplicación útil de auto que mejora la capacidad del lenguaje es crear temporarios en macros independientes de tipo. Esto es algo que realmente no podías hacer antes, pero puedes hacerlo ahora.


44
Lo lograste. Ojalá pudiera darle a esto un +2.
cmaster

Una buena respuesta de "ser malditamente cauteloso". @cmaster: Ahí está.
Deduplicador

He encontrado un caso más útil: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist

14

Sí, hace que sea más fácil saber el tipo de su variable si no la usa auto. La pregunta es: ¿ necesita saber el tipo de su variable para leer el código? A veces la respuesta será sí, a veces no. Por ejemplo, al obtener un iterador de a std::vector<int>, ¿necesita saber que es un std::vector<int>::iteratoro sería auto iterator = ...;suficiente? Todo lo que cualquiera quisiera hacer con un iterador está dado por el hecho de que es un iterador, simplemente no importa cuál sea el tipo específicamente.

Úselo autoen esas situaciones cuando no haga que su código sea más difícil de leer.


12

Personalmente lo uso autosolo cuando es absolutamente obvio para el programador lo que es.

Ejemplo 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Ejemplo 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

55
Este es un claro ejemplo de mal nombre que perjudica la legibilidad, no realmente automático. Nadie tiene la menor idea de lo que hace "DoSomethingWeird", por lo que usar auto o no no lo hará más legible. Tendrá que consultar los documentos de cualquier manera.
R. Martinho Fernandes

44
Ok, es algo mejor ahora. Sin embargo, todavía encuentro que la variable está mal nombrada, lo que todavía duele. Si escribiera auto record = myObj.FindRecord(something), quedaría claro que el tipo de variable fue registro. O nombrarlo ito similar dejaría en claro que devuelve un iterador. Tenga en cuenta que, incluso si no lo usó auto, nombrar correctamente la variable significaría que no necesita volver a la declaración para ver el tipo desde cualquier lugar de la función . Eliminé mi voto negativo porque el ejemplo no es un hombre de paja completo ahora, pero todavía no compro el argumento aquí.
R. Martinho Fernandes

2
Para agregar a @ R.MartinhoFernandes: la pregunta es, ¿es realmente importante ahora QUÉ es exactamente un "registro"? Es Creo que más importante que es un registro, el tipo primitivo subyacente real es otra capa de abstracción .. Así que uno sería sin función automática probable que tenga:MyClass::RecordTy record = myObj.FindRecord (something)
paul23

2
@ paul23: ¿Qué gana el uso de auto versus el tipo? Entonces, si su única objeción es "No sé cómo usar esto". Cualquiera de los dos te hace buscarlo de todos modos.
GManNickG

3
@GManNickG me dice el tipo exacto de falta de importancia.
paul23

10

Esta pregunta solicita opinión, que variará de un programador a otro, pero yo diría que no. De hecho, en muchos casos, todo lo contrario autopuede ayudar a que el código sea más fácil de entender al permitir que el programador se centre en la lógica en lugar de las minucias.

Esto es especialmente cierto frente a los tipos de plantillas complejas. Aquí hay un ejemplo simplificado y artificial. ¿Cuál es más fácil de entender?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. o ...

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

Algunos dirían que el segundo es más fácil de entender, otros pueden decir el primero. Sin embargo, otros podrían decir que un uso gratuito de autopuede contribuir a una tontería de los programadores que lo usan, pero esa es otra historia.


44
+1 Jaja, todos están presentando std::mapejemplos, además con argumentos de plantilla complejos.
Nawaz

1
@Nawaz: Es fácil encontrar nombres de plantillas largos y locos con maps. :)
John Dibling

@Nawaz: pero me pregunto por qué entonces nadie viene con un rango basado en bucles como la alternativa mejor y más legible ...
PlasmaHH

1
@PlasmaHH, no todos los bucles con iteradores pueden reemplazarse por rangos, por forejemplo, si los iteradores están invalidados en el cuerpo del bucle y, por lo tanto, deben pre-incrementarse o no incrementarse en absoluto.
Jonathan Wakely

@PlasmaHH: en mi caso, MSVC10 no hace rangos para bucles. Dado que MSVC10 es mi banco de pruebas de C ++ 11, realmente no tengo mucha experiencia con ellos.
John Dibling

8

Muchas buenas respuestas hasta ahora, pero para centrarme en la pregunta original, creo que Herb va demasiado lejos en su consejo para usarlo autolibremente. Su ejemplo es un caso donde el uso autoobviamente perjudica la legibilidad. Algunas personas insisten en que no es un problema con los IDE modernos donde puede pasar el mouse sobre una variable y ver el tipo, pero no estoy de acuerdo: incluso las personas que siempre usan un IDE a veces necesitan mirar fragmentos de código de forma aislada (piense en las revisiones de código , por ejemplo) y un IDE no ayudará.

En pocas palabras: se usa autocuando ayuda: es decir, iteradores en bucles for. No lo use cuando el lector tenga dificultades para averiguar el tipo.


6

Estoy bastante sorprendido de que nadie haya señalado aún que el auto ayuda si no hay un tipo claro. En este caso, puede solucionar este problema utilizando un #define o typedef en una plantilla para encontrar el tipo utilizable real (y esto a veces no es trivial), o simplemente usa auto.

Supongamos que tiene una función, que devuelve algo con un tipo específico de plataforma:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

¿Qué uso de brujas preferirías?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

o simplemente

auto stuff = getStuff();

Claro que puedes escribir

#define StuffType (...)

también en alguna parte, pero

StuffType stuff = getStuff();

¿realmente contar algo más sobre el tipo de x? Le dice que es lo que se devuelve desde allí, pero es exactamente lo que es automático. Esto es redundante: 'cosas' se escriben 3 veces aquí, esto en mi opinión lo hace menos legible que la versión 'automática'.


55
La forma correcta de manejar tipos específicos de plataforma es para typedefellos.
cmaster

3

La legibilidad es subjetiva; necesitará observar la situación y decidir qué es lo mejor.

Como señaló, sin auto, las declaraciones largas pueden producir mucho desorden. Pero como también señaló, las declaraciones breves pueden eliminar la información de tipo que puede ser valiosa.

Además de esto, también agregaría esto: asegúrese de que esté viendo la legibilidad y no la capacidad de escritura. El código que es fácil de escribir generalmente no es fácil de leer y viceversa. Por ejemplo, si estuviera escribiendo, preferiría auto. Si estuviera leyendo, tal vez las declaraciones más largas.

Entonces hay consistencia; ¿Qué tan importante es para ti? ¿Desea auto en algunas partes y declaraciones explícitas en otras, o un método consistente en todas partes?


2

Tomaré el punto de código menos legible como una ventaja, y alentaré al programador a usarlo cada vez más. ¿Por qué? Claramente, si el código que usa auto es difícil de leer, entonces también será difícil de escribir. El programador se ve obligado a utilizar el nombre de la variable significativa para mejorar su trabajo.
Quizás al principio el programador no escriba los nombres de las variables significativas. Pero eventualmente mientras corrige los errores, o en la revisión del código, cuando él / ella tiene que explicar el código a otros, o en un futuro no tan cercano, él / ella explica el código a las personas de mantenimiento, el programador se dará cuenta del error y usará El nombre de la variable significativa en el futuro.


2
En el mejor de los casos, conseguiría que las personas escribieran nombres de variables como myComplexDerivedTypepara compensar el tipo que falta, lo que satura el código por la repetición de tipos (en todos los lugares donde se usa la variable), y que incita a las personas a omitir el propósito de la variable en su nombre . Mi experiencia es que no hay nada tan improductivo como poner obstáculos activamente en el código.
cmaster

2

Tengo dos pautas:

  • Si el tipo de la variable es obvio, tedioso de escribir o difícil de determinar, use auto.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
  • Si necesita una conversión específica o el tipo de resultado no es obvio y puede causar confusión.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious

0

Si. Disminuye la verbosidad, pero el malentendido común es que la verbosidad disminuye la legibilidad. Esto solo es cierto si considera que la legibilidad es estética en lugar de su capacidad real de interpretar código, que no se incrementa mediante el uso de auto. En el ejemplo más comúnmente citado, los iteradores de vectores, puede parecer en la superficie que el uso de auto aumenta la legibilidad de su código. Por otro lado, no siempre sabes lo que te dará la palabra clave automática. Tienes que seguir la misma ruta lógica que el compilador para hacer esa reconstrucción interna, y muchas veces, en particular con iteradores, vas a hacer suposiciones incorrectas.

Al final del día, 'auto' sacrifica la legibilidad del código y la claridad, por la 'limpieza' sintáctica y estética (que solo es necesaria porque los iteradores tienen una sintaxis complicada innecesariamente) y la capacidad de escribir 10 caracteres menos en cualquier línea. No vale la pena el riesgo o el esfuerzo involucrado a largo plazo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.