Me estoy moviendo hacia C ++ 11 desde C ++ 98 y me he familiarizado con la auto
palabra clave. Me preguntaba por qué necesitamos declarar explícitamente auto
si el compilador puede deducir automáticamente el tipo. Sé que C ++ es un lenguaje fuertemente tipado y esta es una regla, pero ¿no fue posible lograr el mismo resultado sin declarar explícitamente una variable auto
?
80
Respuestas:
Dejar lo explícito
auto
rompería el lenguaje:p.ej
int main() { int n; { auto n = 0; // this shadows the outer n. } }
donde se puede ver que dejar caer el
auto
no sombrearía el exteriorn
.fuente
n := 0
para introducir una nueva variable. Por quéauto
se utiliza es una pregunta basada en opiniones.:=
). (d) Ya encaja en la gramática. Creo que aquí hay muy poco espacio para opinar.x = f()
declara una nueva variable (si aún no existe), obteniendo el tipo de valor de retorno de f ... Sin embargo, requerir que auto declare explícitamente una variable, reduce el riesgo de declarar nuevas variables por accidente (por ejemplo, debido a un error tipográfico ...).Tu pregunta permite dos interpretaciones:
Betsabé respondió muy bien a la primera interpretación, para la segunda, considere lo siguiente (asumiendo que no existen otras declaraciones hasta ahora; C ++ hipotéticamente válido):
int f(); double g(); n = f(); // declares a new variable, type is int; d = g(); // another new variable, type is double if(n == d) { n = 7; // reassigns n auto d = 2.0; // new d, shadowing the outer one }
Se podría ser posible, otros idiomas se salen bastante bien con (bueno, aparte de la cuestión de sombreado tal vez) ... No es por lo que en C ++, sin embargo, y la cuestión (en el sentido de la segunda interpretación) ahora es: ¿Por qué ?
Esta vez, la respuesta no es tan evidente como en la primera interpretación. Sin embargo, una cosa es obvia: el requisito explícito de la palabra clave hace que el idioma sea más seguro (no sé si esto fue lo que llevó al comité de idiomas a su decisión, pero sigue siendo un punto):
grummel = f(); // ... if(true) { brummel = f(); //^ uh, oh, a typo... }
¿Podemos estar de acuerdo en que esto no necesita más explicaciones?
(citó el comentario de psmears debido a su importancia, gracias por insinuar)
fuente
auto
, en mi opinión, es que significa que agregar una variable global en un lugar alejado de una función (por ejemplo, en un archivo de encabezado) puede convertir lo que estaba destinado a ser la declaración de un ámbito local variable en esa función en una asignación a la variable global ... con consecuencias potencialmente desastrosas (y ciertamente muy confusas).global <variable>
declaración.) Eso requeriría aún más modificaciones al lenguaje C ++, por supuesto, por lo que probablemente no sería factible.MOV R6 R5
SUB #nnn R6
en un PDP-11 asumiendo que R5 se usa como puntero de trama y R6 es el puntero de pila. nnn es la cantidad de bytes de almacenamiento necesarios.Voy a reformular ligeramente tu pregunta de una manera que te ayude a entender por qué necesitas
auto
:¿No fue posible ? Por supuesto que era "posible". La pregunta es si valdría la pena el esfuerzo de hacerlo.
La mayoría de las sintaxis de otros lenguajes que no utilizan nombres de tipos funcionan de dos formas. Existe la forma de Go-like, donde
name := value;
declara una variable. Y existe la forma similar a Python, dondename = value;
declara una nueva variable siname
no ha sido declarada previamente.Supongamos que no hay problemas sintácticos al aplicar cualquiera de las sintaxis a C ++ (aunque ya puedo ver que
identifier
seguido de:
en C ++ significa "hacer una etiqueta"). Entonces, ¿qué pierde en comparación con los marcadores de posición?Bueno, ya no puedo hacer esto:
auto &name = get<0>(some_tuple);
Ver,
auto
siempre significa "valor". Si desea obtener una referencia, debe usar explícitamente un&
. Y, con razón, fallará al compilar si la expresión de asignación es un prvalue. Ninguna de las sintaxis basadas en asignaciones tiene una forma de diferenciar entre referencias y valores.Ahora, podría hacer que tales sintaxis de asignación deduzcan referencias si el valor dado es una referencia. Pero eso significaría que no puedes hacer:
auto name = get<0>(some_tuple);
Esto copia de la tupla, creando un objeto independiente de
some_tuple
. A veces, eso es exactamente lo que quieres. Esto es aún más útil si desea pasar de la tupla conauto name = get<0>(std::move(some_tuple));
.Bien, tal vez podamos extender un poco estas sintaxis para dar cuenta de esta distinción. Quizás
&name := value;
o&name = value;
quisiera deducir una referencia comoauto&
.Está bien. ¿Qué pasa con esto?
decltype(auto) name = some_thing();
Oh, es cierto; C ++ en realidad tiene dos marcadores de posición:
auto
ydecltype(auto)
. La idea básica de esta deducción es que funciona exactamente como si lo hubiera hechodecltype(expr) name = expr;
. Entonces, en nuestro caso, sisome_thing()
es un objeto, deducirá un objeto. Sisome_thing()
es una referencia, deducirá una referencia.Esto es muy útil cuando está trabajando en un código de plantilla y no está seguro de cuál será exactamente el valor de retorno de una función. Esto es excelente para el reenvío y es una herramienta esencial, incluso si no se usa ampliamente.
Entonces ahora necesitamos agregar más a nuestra sintaxis.
name ::= value;
significa "hacer lo quedecltype(auto)
hace". No tengo un equivalente para la variante Pythonic.Mirando esta sintaxis, ¿no es bastante fácil escribir mal accidentalmente? No solo eso, difícilmente se auto documenta. Incluso si nunca lo has visto
decltype(auto)
antes, es lo suficientemente grande y obvio como para que al menos puedas decir fácilmente que está sucediendo algo especial. Considerando que la diferencia visual entre::=
y:=
es mínima.Pero eso es cosa de opinión; hay cuestiones más sustantivas. Mira, todo esto se basa en el uso de la sintaxis de asignación. Bueno ... ¿qué pasa con los lugares donde no puede usar la sintaxis de asignación? Me gusta esto:
for(auto &x : container)
¿Cambiamos eso a
for(&x := container)
? Porque eso parece decir algo muy diferente al basado en rangofor
. Parece que es la declaración inicializadora de unfor
bucle regular , no una basada en rangofor
. También sería una sintaxis diferente de los casos no deducidos.Además, la inicialización de la copia (usando
=
) no es lo mismo en C ++ que la inicialización directa (usando la sintaxis del constructor). Por lo quename := value;
puede que no funcione en los casos en que loauto name(value)
habría hecho.Claro, podría declarar que
:=
usará la inicialización directa, pero eso sería bastante en congruente con la forma en que se comporta el resto de C ++.Además, hay una cosa más: C ++ 14. Nos dio una función de deducción útil: la deducción por tipo de devolución. Pero esto se basa en marcadores de posición. Al igual que el basado en rango
for
, se basa fundamentalmente en un nombre de tipo que se completa con el compilador, no mediante una sintaxis aplicada a un nombre y expresión en particular.Mira, todos estos problemas provienen de la misma fuente: estás inventando una sintaxis completamente nueva para declarar variables. Las declaraciones basadas en marcadores de posición no tenían que inventar una nueva sintaxis . Están usando exactamente la misma sintaxis que antes; simplemente emplean una nueva palabra clave que actúa como un tipo, pero tiene un significado especial. Esto es lo que le permite funcionar en función de rango
for
y para la deducción por tipo de devolución. Es lo que le permite tener múltiples formas (auto
vs.decltype(auto)
). Etcétera.Los marcadores de posición funcionan porque son la solución más simple al problema, al mismo tiempo que retienen todos los beneficios y la generalidad de usar un nombre de tipo real. Si se le ocurrió otra alternativa que funcionara tan universalmente como lo hacen los marcadores de posición, es muy poco probable que sea tan simple como los marcadores de posición.
A menos que solo fuera deletrear marcadores de posición con diferentes palabras clave o símbolos ...
fuente
auto
declaraciones / deducción del valor de retorno.En resumen:
auto
podría descartarse en algunos casos, pero eso generaría inconsistencias.En primer lugar, como se señaló, la sintaxis de declaración en C ++ es
<type> <varname>
. Las declaraciones explícitas requieren algún tipo o al menos una palabra clave de declaración en su lugar. Así que podríamos usarvar <varname>
odeclare <varname>
o algo, peroauto
es una palabra clave de larga data en C ++, y es un buen candidato para palabra clave de deducción automática de tipo.¿Es posible declarar implícitamente variables por asignación sin romper todo?
A veces sí. No puede realizar asignaciones fuera de funciones, por lo que podría usar la sintaxis de asignación para declaraciones allí. Pero tal enfoque traería inconsistencia al lenguaje, posiblemente conduciendo a errores humanos.
a = 0; // Error. Could be parsed as auto declaration instead. int main() { return 0; }
Y cuando se trata de cualquier tipo de variables locales, las declaraciones explícitas son una forma de controlar el alcance de una variable.
a = 1; // use a variable declared before or outside auto b = 2; // declare a variable here
Si se permitiera una sintaxis ambigua, la declaración de variables globales podría convertir repentinamente las declaraciones implícitas locales en asignaciones. Encontrar esas conversiones requeriría verificar todo . Y para evitar colisiones, necesitaría nombres únicos para todos los globales, lo que destruye toda la idea de alcance. Entonces es realmente malo.
fuente
auto
es una palabra clave que puede usar en lugares donde normalmente necesita especificar un tipo .int x = some_function();
Puede hacerse más genérico haciendo que el
int
tipo se deduzca automáticamente:auto x = some_function();
Entonces es una extensión conservadora del lenguaje; encaja en la sintaxis existente. Sin él se
x = some_function()
convierte en una declaración de asignación, ya no en una declaración.fuente
la sintaxis debe ser inequívoca y también compatible con versiones anteriores.
Si se descarta auto, no habrá forma de distinguir entre declaraciones y definiciones.
auto n = 0; // fine n=0; // statememt, n is undefined.
fuente
auto
ya era una palabra clave (pero con un significado obsoleto), por lo que no rompió el código usándolo como nombre. Esa es una razón por la que no se eligió una palabra clave mejor, comovar
olet
, en su lugar.auto
es en realidad una palabra clave bastante excelente para esto: expresa exactamente lo que significa, es decir, reemplaza el nombre de un tipo con "el tipo automático". Con una palabra clave comovar
olet
, por lo tanto, debe requerir la palabra clave incluso si el tipo se especifica explícitamente, es decir,var int n = 0
o algo asívar n:Int = 0
. Básicamente, así es como se hace en Rust.auto
es definitivamente excelente en el contexto de la sintaxis existente, diría que algo comovar int x = 42
ser la definición de variable básica, convar x = 42
yint x = 42
como abreviaturas, tendría más sentido que la sintaxis actual si se considera fuera del contenido histórico. Pero sobre todo es cuestión de gustos. Pero, tienes razón, debería haber escrito "una de las razones" en lugar de "una razón" en mi comentario original :)auto
, hay un tipo automático (uno diferente, dependiendo de la expresión).Agregando a las respuestas anteriores, una nota extra de un viejo pedo: parece que puede verlo como una ventaja poder comenzar a usar una nueva variable sin declararla de ninguna manera.
En lenguajes con la posibilidad de definición implícita de variables, esto puede ser un gran problema, especialmente en sistemas más grandes. Comete un error tipográfico y depura durante horas solo para descubrir que introdujo involuntariamente una variable con un valor de cero (o peor) -
blue
vsbleu
,label
vslable
... el resultado es que realmente no puede confiar en ningún código sin una verificación exhaustiva precisa nombres de variables.El solo uso
auto
le dice tanto al compilador como al mantenedor que es su intención declarar una nueva variable.Piénselo, para poder evitar este tipo de pesadillas, la declaración 'ninguno implícito' se introdujo en FORTRAN, y la ve en todos los programas FORTRAN serios hoy en día. No tenerlo es simplemente ... aterrador.
fuente