Parece que autofue 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 autopara 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,
autotiene 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
resultes. 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
autocomo 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 problemaautoy no un problema con los temporales de subexpresión? ¿Por qué esauto result = foo();malo, perofoo().bar()no?Esto no es
autoexactamente 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) tratanautocomo un salvador para los tipos y apagan su cerebro cuando lo usan, o b) olvidan queautosiempre 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
autoen absoluto, por lo que no los considero argumentos legítimos en su contra. Pero parece queautohace 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:
autodeduce 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_referencelo 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.= deleteesa 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 usarvalidsin 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
SomeTypeserloVector, 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 aVectorantes del final de su expresión completa. Si tenemos este código, todo está bien:Sin embargo, si hubiéramos usado
autoaquí, podríamos tener problemas:fuente
autotiene 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.autoantes, específicamente con la biblioteca Eigen. Es especialmente complicado porque el problema a menudo no aparece en las compilaciones de depuración.autotambié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 conautoautogeneralmente implica algún tipo de verificación de tipo (y las salpicadurasautoen 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_iteratorconauto. Obtendrá un iterador ordinario (no constante) en este ejemplo de código tomado de esta pregunta :fuente
iteratoren 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
doSomethingWithDatafunció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
autoes 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
autoesa reducen el tipeo y engañan a las personas para que piensen que deberían comenzar a ser flojos con el tipeo.En los
autodías anteriores , las personas usabantypedefs, lo cual fue genial porquetypedefpermitió 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 usaautocomo 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 beRealmente 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 eltypedefuno.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 quebitsetse implementa? ¿O considera que los contenedores de bits no tienen sentido?autono 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 seBderiva deAy una función que regresa deArepente regresaB, entonces seautocomporta 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
autopodría cambiar el comportamiento del código , por lo que será mejor que tenga cuidado.fuente
autotiene 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)? Elautocódigo continuará funcionando, pero producirá resultados inesperados.boollugar deautocambiar 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 seboolproduce en la declaración de variable en lugar de en la evaluación de la condición en elif. Si se establece elenumalcance, la conversión aboolno se realizará sin un aviso explícito.La palabra clave
autosimplemente deduce el tipo del valor de retorno. Por lo tanto, no es equivalente a un objeto Python, p. Ej.Como
autose 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.autoes 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
Cno 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
autoes la primera vez 1 esto cambia!Imagine que lo utilizó antes
autocomo 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, usarloautocomo 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 soloautopuedes 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
autohace, nunca inferir una referencia ni unconsttipo. Comoautoreferencia, 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]), porqueies un int firmado. Para evitar esto, debe escribir, por ejemploo tal vez mejor:
fuente
sizedevolucionessize_t, tendrías que poder tener un mesize_tgusta literal0z. Pero puede declarar un UDL para hacer esto. (size_t operator""_z(...))unsignedes muy probable que no sea lo suficientemente grande como para contener todos los valores destd::size_tlas arquitecturas convencionales, por lo que en el improbable caso de que alguien tuviera un contenedor con un número absurdamente gigantesco de elementos, el usounsignedpodría causar un bucle infinito en el rango inferior de índices. Si bien es poco probable que esto sea un problema,std::size_tdebe usarse para obtener un código limpio que indique la intención correctamente. No estoy seguro de que inclusounsigned long longsea estrictamente garantizado que sea suficiente, aunque en la práctica presumiblemente debe ser lo mismo.unsigned long longse garantiza que sea de al menos 64 bits, pero en teoríasize_tpodrí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
autoes 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 hacerlotemplateo 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 inaceptableinttan fácil de ver aquí y escribirintes 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
autodedujo el tipo de variable factorial comointporque se le asignó1.fuente