Pregunta ¿Por qué tengo que escribir explícitamente la palabra clave 'auto'?


Me estoy moviendo hacia C ++ 11 desde C ++ 98 y me he familiarizado con el auto palabra clave. Me preguntaba por qué tenemos que 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?


75
2018-05-23 08:15


origen


Respuestas:


Dejar caer 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 dejando caer el auto no lo haría sombra el exterior n.


154
2018-05-23 08:16



Tu pregunta permite dos interpretaciones:

  • ¿Por qué necesitamos 'auto' en absoluto? ¿No podemos simplemente dejarlo caer?
  • ¿Por qué estamos obligados a usar auto? ¿No podemos simplemente tenerlo implícito, si no se da?

Bathsheba contestada muy bien la primera interpretación, para el segundo, considere lo siguiente (suponiendo que no existan otras declaraciones hasta el momento; hipotéticamente C ++ 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
}

Eso haría ser posible, otros lenguajes se salen bastante bien con (bueno, aparte del problema de la sombra quizás) ... No es así en C ++, sin embargo, y la pregunta (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 para la palabra clave hace que el lenguaje sea más seguro (no sé si esto es lo que llevó al comité de lenguaje a su decisión, pero sigue siendo un punto):

grummel = f();

// ...

if(true)
{
    brummel = f();
  //^ uh, oh, a typo...
}

¿Podemos estar de acuerdo en esto sin necesitar más explicaciones?

El mayor peligro de no requerir auto, [sin embargo], 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 se pretendía ser la declaración de un local- variable de alcance en esa función en una asignación a la variable global ... con consecuencias potencialmente desastrosas (y ciertamente muy confusas).

(citado psmears ' comentar debido a su importancia - gracias por insinuar)


37
2018-05-23 08:47



no fue posible lograr el mismo resultado sin declarar explícitamente una variable auto?

Voy a reformular tu pregunta ligeramente de una manera que te ayude a entender por qué necesitas auto:

¿No fue posible lograr el mismo resultado sin explícitamente usando un marcador de posición de tipo?

¿No fue posible? Por supuesto que fue "posible". La pregunta es si valdría la pena el esfuerzo de hacerlo.

La mayoría de las sintaxis en otros idiomas que no escriben nombres funcionan de dos maneras. Hay una forma de ir, donde name := value; declara una variable. Y está la forma de Python, donde name = value; declara una nueva variable si name no ha sido declarado previamente.

Supongamos que no hay problemas sintácticos con la aplicación de sintaxis a C ++ (aunque ya puedo ver que identifier seguido por :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 no podrá compilar correctamente si la expresión de asignación es un valor prve. Ninguna de las sintaxis basadas en tareas tiene una forma de diferenciar entre referencias y valores.

Ahora bien, 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);

Esta copias 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 con auto name = get<0>(std::move(some_tuple));.

OK, entonces tal vez podríamos extender estas sintaxis un poco para tener en cuenta esta distinción. Tal vez &name := value; o &name = value; significaría deducir una referencia como auto&.

Está bien. ¿Qué tal esto?

decltype(auto) name = some_thing();

Oh, es cierto; C ++ en realidad tiene dos marcadores de posición: auto y decltype(auto). La idea básica de esta deducción es que funciona exactamente como si hubieras hecho decltype(expr) name = expr;. Entonces en nuestro caso, si some_thing() es un objeto, deducirá un objeto. Si some_thing() es una referencia, deducirá una referencia.

Esto es muy útil cuando trabajas en código de plantilla y no estás seguro de cuál será el valor de retorno de una función. Esto es ideal 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 qué decltype(auto) does ". No tengo un equivalente para la variante Pythonic.

En cuanto a esta sintaxis, ¿no es bastante fácil equivocarse accidentalmente? No solo eso, es difícil auto-documentarse. Incluso si nunca has visto decltype(auto) antes, es lo suficientemente grande y obvio que al menos puedes decir fácilmente que está sucediendo algo especial. Considerando que la diferencia visual entre ::= y := es mínimo

Pero eso es opinión; hay más problemas sustantivos. Mira, todo esto se basa en el uso de la sintaxis de asignación. Bueno ... ¿qué pasa con los lugares donde hipocresía 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 de rango basado for. Parece que es la declaración del inicializador de un for loop, no basado en el rango for. También sería una sintaxis diferente de los casos no deducidos.

Además, copia-inicialización (usando =) no es lo mismo en C ++ que la inicialización directa (usando la sintaxis del constructor). Asi que name := value; puede no funcionar en casos donde auto name(value) tendría.

Claro, podrías declarar eso := usará la inicialización directa, pero eso sería bastante congruente con la forma en que se comporta el resto de C ++.

Además, hay una cosa más: C ++ 14. Nos dio una característica de deducción útil: deducción del tipo de devolución. Pero esto se basa en marcadores de posición. Tanto como el rango basado en for, se basa fundamentalmente en un nombre de tipo que el compilador completa, 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 nuevas sintaxis. Están usando la misma sintaxis exacta que antes; solo están empleando una nueva palabra clave que actúa como un tipo, pero tiene un significado especial. Esto es lo que le permite trabajar en rango for y para la deducción del 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 tiempo que conservan todos los beneficios y la generalidad de usar un nombre de tipo real. Si se le ocurría 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 ...


14
2018-05-24 02:32



En breve: auto podría eliminarse en algunos casos, pero eso daría lugar a incoherencias.

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. Entonces podríamos usar var <varname> o declare <varname> o algo así, pero auto es una palabra clave de larga duración en C ++, y es un buen candidato para la palabra clave de deducción automática.

¿Es posible declarar implícitamente variables por asignación sin romper todo?

A veces sí. No puede realizar tareas fuera de las funciones, por lo que podría utilizar la sintaxis de asignación para las 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 la manera 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 permitió la sintaxis ambigua, declarar variables globales podría convertir repentinamente declaraciones locales implícitas en asignaciones. Encontrar esas conversiones requeriría verificar todo. Y para evitar colisiones, necesitaría nombres únicos para todos los globales, lo que destruye la idea general del alcance. Entonces es realmente malo.


12
2018-05-23 13:55



auto es una palabra clave que puede usar en lugares donde normalmente necesita especificar un tipo.

  int x = some_function();

Se puede hacer más genérico haciendo el int tipo deducido automáticamente:

  auto x = some_function();

Entonces es una extensión conservadora del lenguaje; se ajusta a la sintaxis existente. Sin ello x = some_function() se convierte en una declaración de asignación, ya no es una declaración.


11
2018-05-23 08:28



la sintaxis debe ser inequívoca y también retrocompatible.

Si se elimina automáticamente, no habrá forma de distinguir entre enunciados y definiciones.

auto n = 0; // fine
n=0; // statememt, n is undefined.

9
2018-05-23 08:24



Agregando a respuestas anteriores, una nota extra de un viejo pedo: Parece que puede ser una ventaja el poder comenzar a usar una nueva variable sin declararla de ninguna manera.

En los idiomas con la posibilidad de una definición implícita de variables, esto puede ser un gran problema, especialmente en sistemas más grandes. Hace un error tipográfico y depura durante horas solo para descubrir que ha introducido involuntariamente una variable con un valor de cero (o peor) - blue vs bleu, label vs lable ... el resultado es que no se puede confiar en ningún código sin una verificación minuciosa de los nombres de las variables precisas.

Solo usando auto le dice al compilador y al mantenedor que es su intención declarar una nueva variable.

Piénselo, para poder evitar este tipo de pesadillas, la declaración de 'ninguno implícito' fue introducida en FORTRAN, y verá que se usa en todos los programas FORTRAN serios hoy en día. No tenerlo es simplemente ... aterrador.


3
2018-05-30 00:03