Parece que auto
fue una característica bastante importante para agregar en C ++ 11 que parece seguir muchos de los lenguajes más nuevos. Al igual que con un lenguaje como Python, no he visto ninguna declaración explícita de variables (no estoy seguro de si es posible usar los estándares de Python).
¿Hay algún inconveniente en usar auto
para declarar variables en lugar de declararlas explícitamente?
c++
c++11
type-inference
auto
DxAlpha
fuente
fuente
Respuestas:
Solo ha preguntado acerca de los inconvenientes, por lo que estoy destacando algunos de ellos. Cuando se usa bien,
auto
tiene varias ventajas también. Los inconvenientes resultan de la facilidad de abuso y del mayor potencial para que el código se comporte de manera no intencional.El principal inconveniente es que, al usar
auto
, no necesariamente conoce el tipo de objeto que se está creando. También hay ocasiones en las que el programador puede esperar que el compilador deduzca un tipo, pero el compilador deduce firmemente otro.Dada una declaración como
no necesariamente tienes conocimiento de qué tipo
result
es. Puede ser unint
. Puede ser un puntero. Puede ser otra cosa. Todos ellos admiten diferentes operaciones. También puede cambiar drásticamente el código mediante un cambio menor comoporque, dependiendo de las sobrecargas que existan para
CallSomeFunction()
el tipo de resultado, puede ser completamente diferente y, por lo tanto, el código posterior puede comportarse de manera completamente diferente de lo previsto. Es posible que de repente active mensajes de error en el código posterior (p. Ej., Posteriormente intente desreferenciar un mensajeint
, intente cambiar algo que es ahoraconst
). El cambio más siniestro es donde su cambio pasa por el compilador, pero el código posterior se comporta de formas diferentes y desconocidas, posiblemente con errores.Por lo tanto, no tener un conocimiento explícito del tipo de algunas variables hace que sea más difícil justificar rigurosamente una afirmación de que el código funciona según lo previsto. Esto significa un mayor esfuerzo para justificar las afirmaciones de "apto para el propósito" en dominios de alta criticidad (por ejemplo, críticos de seguridad o críticos de misión).
El otro inconveniente más común es la tentación de un programador de utilizarlo
auto
como un instrumento contundente para forzar la compilación del código, en lugar de pensar en lo que está haciendo el código y trabajar para hacerlo bien.fuente
auto
, ¡entonces la mayoría de los lenguajes de pato sufren tal inconveniente por diseño!CallSomeFunction()
devuelve un tipo diferente dependiendo de la secuencia de sus argumentos, ese es un defecto de diseñoCallSomeFunction()
, no un problema deauto
. Si no lee la documentación de una función que está utilizando antes de usarla, es un defecto del programador, no un problemaauto
. - Pero entiendo que estás jugando al abogado del diablo aquí, es solo que Nir Friedman tiene el caso mucho mejor.T CallSomeFunction(T, int, int)
un defecto de diseño? Obviamente, "devuelve un tipo diferente dependiendo de la secuencia de sus argumentos".auto
, no necesariamente conoce el tipo de objeto que se está creando". ¿Puede explicar por qué esto es un problemaauto
y no un problema con los temporales de subexpresión? ¿Por qué esauto result = foo();
malo, perofoo().bar()
no?Esto no es
auto
exactamente un inconveniente de una manera basada en principios, pero en términos prácticos parece ser un problema para algunos. Básicamente, algunas personas: a) tratanauto
como un salvador para los tipos y apagan su cerebro cuando lo usan, o b) olvidan queauto
siempre se deduce el valor de los tipos. Esto hace que las personas hagan cosas como esta:Vaya, solo copiamos en profundidad algún objeto. A menudo es un error o un fallo de rendimiento. Luego, también puedes girar hacia el otro lado:
Ahora obtienes una referencia colgante. Estos problemas no son causados
auto
en absoluto, por lo que no los considero argumentos legítimos en su contra. Pero parece queauto
hace que este problema sea más común (desde mi experiencia personal), por las razones que enumeré al principio.Creo que, con el tiempo, la gente se ajustará y comprenderá la división del trabajo:
auto
deduce el tipo subyacente, pero aún así desea pensar en la referencia y la constancia. Pero está tomando un poco de tiempo.fuente
std::vector
). Ser costoso de copiar no es una propiedad de una clase, sino de objetos individuales. Pormethod_that_returns_reference
lo tanto, podría referirse a un objeto de una clase que tiene un constructor de copia, pero cuyo objeto resulta bastante costoso de copiar (y no se puede mover).std::vector
? (Porque podría, sí, o porque no controlas la clase, pero ese no es el punto) Si es costoso copiarlo (y no posee ningún recurso, porque es copiable), ¿por qué no usar COW en el objeto? La localidad de datos ya es eliminada por el tamaño del objeto.= delete
esa sobrecarga. Aunque en general lo que dices es una solución. Este es un tema que he explorado, si está interesado: nirfriedman.com/2016/01/18/… .Otras respuestas mencionan inconvenientes como "realmente no sabes cuál es el tipo de variable". Yo diría que esto está relacionado en gran medida con la convención de nomenclatura descuidada en el código. Si sus interfaces están claramente nombradas, no debería tener que preocuparse por el tipo exacto. Claro,
auto result = callSomeFunction(a, b);
no te dice mucho. Peroauto valid = isValid(xmlFile, schema);
le dice lo suficiente para usarvalid
sin tener que preocuparse de cuál es su tipo exacto. Después de todo, con soloif (callSomeFunction(a, b))
, tampoco sabrías el tipo. Lo mismo con cualquier otro objeto temporal de subexpresión. Así que no considero que esto sea un verdadero inconvenienteauto
.Diría que su principal inconveniente es que a veces, el tipo de retorno exacto no es con el que desea trabajar. En efecto, a veces el tipo de retorno real difiere del tipo de retorno "lógico" como un detalle de implementación / optimización. Las plantillas de expresión son un excelente ejemplo. Digamos que tenemos esto:
Lógicamente, esperaríamos
SomeType
serloVector
, y definitivamente queremos tratarlo como tal en nuestro código. Sin embargo, es posible que para fines de optimización, la biblioteca de álgebra que estamos utilizando implemente plantillas de expresión, y el tipo de retorno real es este:Ahora, el problema es que
MultExpression<Matrix, Vector>
voluntad en toda tienda de probabilidad de que unconst Matrix&
econst Vector&
internos; espera que se convierta a aVector
antes del final de su expresión completa. Si tenemos este código, todo está bien:Sin embargo, si hubiéramos usado
auto
aquí, podríamos tener problemas:fuente
auto
tiene muy pocos inconvenientes, así que mantengo esa fortaleza. Y otros ejemplos de proxies, etc. incluyen varios "constructores de cadenas" y objetos similares encontrados en DSL.auto
antes, específicamente con la biblioteca Eigen. Es especialmente complicado porque el problema a menudo no aparece en las compilaciones de depuración.auto
también puede morder cuando se usa la biblioteca de matriz Armadillo , que hace un uso intensivo de la metaprogramación de plantillas para fines de optimización. Afortunadamente, los desarrolladores han agregado la función .eval () que se puede utilizar para evitar los problemas conauto
auto
generalmente implica algún tipo de verificación de tipo (y las salpicadurasauto
en todas partes eliminan la seguridad de ese tipo al igual que en cualquier otro lugar). No es una buena comparación.Uno de los inconvenientes es que a veces no se puede declarar
const_iterator
conauto
. Obtendrá un iterador ordinario (no constante) en este ejemplo de código tomado de esta pregunta :fuente
iterator
en cualquier caso ya que tu mapa no esconst
. si desea convertirlo a aconst_iterator
, especifique el tipo de variable explícitamente como de costumbre o extraiga un método para que su mapa sea constante en el contexto de sufind
. (Prefiero el último. SRP.)auto city_it = static_cast<const auto&>(map).find("New York")
? o, con C ++ 17,auto city_if = std::as_const(map).find("New York")
.Hace que su código sea un poco más difícil o tedioso de leer. Imagina algo así:
Ahora, para determinar el tipo de salida, tendría que rastrear la firma de la
doSomethingWithData
función.fuente
auto it = vec.begin();
es mucho más fácil de leer que,std::vector<std::wstring>::iterator it = vec.begin();
por ejemplo.Como este desarrollador, odio
auto
. O más bien, odio cómo la gente usa malauto
.Soy de la opinión (fuerte) que
auto
es para ayudarlo a escribir código genérico, no para reducir la escritura .C ++ es un lenguaje cuyo objetivo es permitirle escribir código robusto, no minimizar el tiempo de desarrollo.
Esto es bastante obvio a partir de muchas características de C ++, pero desafortunadamente algunas de las más nuevas como
auto
esa reducen el tipeo y engañan a las personas para que piensen que deberían comenzar a ser flojos con el tipeo.En los
auto
días anteriores , las personas usabantypedef
s, lo cual fue genial porquetypedef
permitió que el diseñador de la biblioteca lo ayudara a determinar cuál debería ser el tipo de retorno, para que su biblioteca funcione como se esperaba. Cuando lo usasauto
, le quitas ese control al diseñador de la clase y en su lugar le pides al compilador que descubra cuál debería ser el tipo, lo que elimina una de las herramientas C ++ más poderosas de la caja de herramientas y corre el riesgo de romper su código.En general, si lo usa
auto
, debería ser porque su código funciona para cualquier tipo razonable , no porque sea demasiado vago para escribir el tipo con el que debería funcionar. Si lo usaauto
como una herramienta para ayudar a la pereza, entonces lo que sucede es que eventualmente comienza a introducir errores sutiles en su programa, generalmente causados por conversiones implícitas que no ocurrieron porque lo usóauto
.Por desgracia, estos errores son difíciles de ilustrar en un breve ejemplo aquí porque su brevedad hace que sean menos convincentes que los ejemplos reales que se presentan en un proyecto de usuario - sin embargo, se producen fácilmente en el código de la plantilla-pesado que esperan ciertas conversiones implícitas a tomar sitio.
Si quieres un ejemplo, hay uno aquí . Sin embargo, una pequeña nota: antes de sentir la tentación de saltar y criticar el código: tenga en cuenta que muchas bibliotecas conocidas y maduras se han desarrollado en torno a conversiones tan implícitas, y están allí porque resuelven problemas que pueden ser difíciles, si no imposibles. para resolver lo contrario. Intenta encontrar una mejor solución antes de criticarlos.
fuente
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be
Realmente no es una buena razón IMO. Los IDE actualizados, Visual Studio 2015, por ejemplo, le permiten verificar el tipo de la variable al pasar el mouse por encimaauto
. Esto es * exactamente * igual que eltypedef
uno.typename std::iterator_traits<It>::value_type
. (2) El punto completo fue que el tipo inferido no necesita ser "exactamente el mismo" que el tipo correcto previsto por el diseñador anterior del código; al usarauto
, está quitando la capacidad del diseñador para especificar el tipo correcto.vector<bool>
tonterías" ... ¿perdón? ¿Cómo crees quebitset
se implementa? ¿O considera que los contenedores de bits no tienen sentido?auto
no tiene inconvenientes per se , y abogo por usarlo (a mano) en todas partes en el nuevo código. Permite que su código compruebe constantemente el tipo y evite sistemáticamente el corte silencioso. (Si seB
deriva deA
y una función que regresa deA
repente regresaB
, entonces seauto
comporta como se espera para almacenar su valor de retorno)Sin embargo, el código anterior a C ++ 11 puede depender de conversiones implícitas inducidas por el uso de variables explícitamente tipadas. Cambiar una variable tipada explícitamente
auto
podría cambiar el comportamiento del código , por lo que será mejor que tenga cuidado.fuente
auto
tiene inconvenientes per se (o al menos, muchos piensan que sí). Considere el ejemplo dado en la segunda pregunta en este panel de discusión con Sutter, Alexandrescu y Meyers: Si tieneauto x = foo(); if (x) { bar(); } else { baz(); }
yfoo()
devuelvebool
, ¿qué sucede si losfoo()
cambios devuelven una enumeración (tres opciones en lugar de dos)? Elauto
código continuará funcionando, pero producirá resultados inesperados.bool
lugar deauto
cambiar algo en caso de una enumeración sin ámbito? Puedo estar equivocado (no puedo verificar aquí), pero creo que la única diferencia es que la conversión sebool
produce en la declaración de variable en lugar de en la evaluación de la condición en elif
. Si se establece elenum
alcance, la conversión abool
no se realizará sin un aviso explícito.La palabra clave
auto
simplemente deduce el tipo del valor de retorno. Por lo tanto, no es equivalente a un objeto Python, p. Ej.Como
auto
se deduce durante la compilación, no tendrá ningún inconveniente en tiempo de ejecución.fuente
type()
hace Python. Deduce el tipo, no crea una nueva variable de ese tipo.decltype
.auto
es para asignación de variables específicamente.Lo que nadie mencionó aquí hasta ahora, pero por sí mismo vale una respuesta si me lo preguntas.
Dado que (incluso si todos deberían saberlo
C != C++
) el código escrito en C puede diseñarse fácilmente para proporcionar una base para el código C ++ y, por lo tanto, puede diseñarse sin demasiado esfuerzo para ser compatible con C ++, esto podría ser un requisito para el diseño.Conozco algunas reglas de las que algunas construcciones bien definidas
C
no son válidasC++
y viceversa. Pero esto simplemente daría lugar a archivos ejecutables rotos y se aplica la conocida cláusula UB, que la mayoría de las veces se nota por bucles extraños que provocan bloqueos o lo que sea (o incluso pueden pasar desapercibidos, pero eso no importa aquí).Pero
auto
es la primera vez 1 esto cambia!Imagine que lo utilizó antes
auto
como especificador de clase de almacenamiento y transfiere el código. Ni siquiera necesariamente (dependiendo de la forma en que se usó) se "rompería"; en realidad podría cambiar silenciosamente el comportamiento del programa.Eso es algo que uno debe tener en cuenta.
1 Al menos la primera vez que tengo conocimiento.
fuente
int
" en C, se merece todas las cosas malas que obtendrá de esto. Y si no confía en él, usarloauto
como un especificador de clase de almacenamiento junto con un tipo le dará un buen error de compilación en C ++ (que es una buena cosa en este caso).Una razón por la que puedo pensar es que pierdes la oportunidad de obligar a la clase que se devuelve. Si su función o método devolvió un largo 64 bits, y solo quería un 32 sin signo int, entonces pierde la oportunidad de controlar eso.
fuente
Como describí en esta respuesta
auto
, a veces puede dar lugar a situaciones funky que no tenía la intención. Tienes que decir explícitamenteauto&
que tienes un tipo de referencia mientras lo haces soloauto
puedes crear un tipo de puntero. Esto puede generar confusión al omitir todo el especificador, lo que da como resultado una copia de la referencia en lugar de una referencia real.fuente
auto
hace, nunca inferir una referencia ni unconst
tipo. Comoauto
referencia, será mejor que lo usesauto&&
. (una referencia universal) Si el tipo no es barato de copiar o posee un recurso, entonces el tipo no debe ser copiable para empezar.Otro ejemplo irritante:
genera una advertencia (
comparison between signed and unsigned integer expressions [-Wsign-compare]
), porquei
es un int firmado. Para evitar esto, debe escribir, por ejemploo tal vez mejor:
fuente
size
devolucionessize_t
, tendrías que poder tener un mesize_t
gusta literal0z
. Pero puede declarar un UDL para hacer esto. (size_t operator""_z(...)
)unsigned
es muy probable que no sea lo suficientemente grande como para contener todos los valores destd::size_t
las arquitecturas convencionales, por lo que en el improbable caso de que alguien tuviera un contenedor con un número absurdamente gigantesco de elementos, el usounsigned
podría causar un bucle infinito en el rango inferior de índices. Si bien es poco probable que esto sea un problema,std::size_t
debe usarse para obtener un código limpio que indique la intención correctamente. No estoy seguro de que inclusounsigned long long
sea estrictamente garantizado que sea suficiente, aunque en la práctica presumiblemente debe ser lo mismo.unsigned long long
se garantiza que sea de al menos 64 bits, pero en teoríasize_t
podría ser más grande que esto, supongo. Por supuesto, si tiene> 2 ^ 64 elementos en su contenedor, entonces puede tener mayores problemas de los que preocuparse ... ;-)Creo que
auto
es bueno cuando se usa en un contexto localizado, donde el lector puede deducir fácilmente su tipo, o bien documentado con un comentario de su tipo o un nombre que infiera el tipo real. Aquellos que no entienden cómo funciona pueden tomarlo de manera incorrecta, como usarlo en lugar de hacerlotemplate
o similar. Aquí hay algunos casos de uso buenos y malos en mi opinión.Buenos usos
Iteradores
Punteros de funciones
Malos usos
Flujo de datos
Firma de función
Casos triviales
fuente
int
, tienes que escribir un carácter más si quieresauto
. Eso es inaceptableint
tan fácil de ver aquí y escribirint
es más corto. Por eso es un caso trivial.Me sorprende que nadie haya mencionado esto, pero supongamos que está calculando el factorial de algo:
Este código generará esto:
Definitivamente ese no fue el resultado esperado. Eso sucedió porque
auto
dedujo el tipo de variable factorial comoint
porque se le asignó1
.fuente