for ejemplo c++ c++11 auto

c++ - ejemplo - decltype



¿Por qué necesito escribir explícitamente la palabra clave ''auto''? (7)

Me estoy moviendo hacia C ++ 11 desde C ++ 98 y me he familiarizado con la palabra clave auto . 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 ?


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

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

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

¿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 en otros idiomas que no tienen nombres de máquina funcionan de una o dos maneras. Está la forma de ir, donde name := value; Declara una variable. Y hay una forma parecida a la de Python, donde name = value; declara una nueva variable si el name no ha sido declarado previamente.

Supongamos que no hay problemas sintácticos con la aplicación de la sintaxis a C ++ (aunque ya puedo ver ese identifier seguido de : en C ++ significa "hacer una etiqueta"). Entonces, ¿qué pierdes comparado 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 utilizar explícitamente un & . Y correctamente no podrá compilar si la expresión de asignación es un prvalue. Ninguna de las sintaxis basadas en asignaciones tiene una manera 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 desde 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 moverse desde la tupla con auto name = get<0>(std::move(some_tuple)); .

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

Está bien. ¿Qué pasa con esto?

decltype(auto) name = some_thing();

Oh, es cierto; C ++ tiene dos marcadores de posición: auto y decltype(auto) . La idea básica de esta deducción es que funciona exactamente como si hubiera 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 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 utiliza ampliamente.

Así que ahora tenemos que añadir más a nuestra sintaxis. name ::= value; significa "hacer lo que decltype(auto) hace". No tengo un equivalente para la variante pitónica.

Mirando esta sintaxis, ¿no es eso bastante fácil de escribir incorrectamente por error? No solo eso, es difícil autodocumentarse. Incluso si nunca antes has visto decltype(auto) , es lo suficientemente grande y obvio que al menos puedes decir fácilmente que está pasando algo especial. Mientras que la diferencia visual entre ::= y := es mínima.

Pero eso es cosa de la opinión; Hay más cuestiones de fondo. Ver, todo esto se basa en el uso de la sintaxis de asignación. Bueno ... ¿qué pasa con los lugares donde no se puede usar la sintaxis de asignación? Me gusta esto:

for(auto &x : container)

¿Cambiamos eso a for(&x := container) ? Porque eso parece estar diciendo algo muy diferente de basado for rango for . Parece que es la sentencia inicializadora de un bucle for regular, no un rango basado for . También sería una sintaxis diferente de los casos no deducidos.

Además, la inicialización de la copia (utilizando = ) no es lo mismo en C ++ que la inicialización directa (utilizando la sintaxis del constructor). Entonces name := value; Es posible que no funcione en los casos en que el auto name(value) tendría.

Claro, usted podría declarar que := usará la inicialización directa, pero eso sería bastante incompatible 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 por tipo de retorno. Pero esto se basa en los marcadores de posición. Tanto como basado for rango for , se basa fundamentalmente en un nombre de tipo que se completa con el compilador, no con una sintaxis aplicada a un nombre y una expresión en particular.

Vea, todos estos problemas provienen de la misma fuente: usted está 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 la misma sintaxis que antes; simplemente 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 función del rango for y para la deducción de tipo de retorno. 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 sencilla al problema, al tiempo que conservan todos los beneficios y la generalidad de usar un nombre de tipo real. Si se te 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 se tratara de deletrear marcadores de posición con diferentes palabras clave o símbolos ...


En resumen: en algunos casos, el auto podría caerse, pero eso llevaría a una inconsistencia.

En primer lugar, como se señaló, la sintaxis de la 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 usar var <varname> o declare <varname> o algo así, pero auto es una palabra clave de larga data en C ++, y es un buen candidato para la palabra clave de deducción de tipo automática.

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

A veces sí. No puede realizar funciones de asignación externa, por lo que podría usar 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 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 locales implícitas en asignaciones. Encontrar esas conversiones requeriría revisar todo . Y para evitar colisiones necesitarías nombres únicos para todos los globales, lo que destruye la idea del alcance. Así que es realmente malo.


Agregando a las respuestas anteriores, una nota adicional de un viejo pedo: parece que puede verlo como una ventaja poder comenzar a usar una nueva variable sin declararla de ninguna manera.

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

El solo uso de auto le dice al compilador y al mantenedor que su intención es declarar una nueva variable.

Piénselo, para poder evitar este tipo de pesadillas, la declaración de ''implícita ninguna'' se introdujo en FORTRAN, y se ve que se utiliza en todos los programas serios de FORTRAN en la actualidad. No tenerlo es simplemente ... aterrador.


Eliminar el auto explícito rompería el lenguaje:

p.ej

int main() { int n; { auto n = 0; // this shadows the outer n. } }

donde se puede ver que soltar el auto no sombrearía la n externa.


La sintaxis tiene que ser inequívoca y también compatible con versiones anteriores.

Si se abandona el modo automático, no habrá manera de distinguir entre declaraciones y definiciones.

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


Tu pregunta permite dos interpretaciones:

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

Betsabé answered muy bien a la primera interpretación, para la segunda, considere lo siguiente (suponiendo que no existan otras declaraciones hasta el momento; 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 }

Sería posible, otros idiomas saldrían bastante bien con (bueno, aparte del problema de la sombra quizás) ... Sin embargo, no es así en C ++, 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 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 tomar una decisión, aún sigue siendo un punto):

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

¿Podemos ponernos de acuerdo en que no necesitamos más explicaciones?

El peligro aún mayor 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 activar lo que se pretendía que fuera la declaración de un local. variable de ámbito en esa función en una asignación a la variable global ... con consecuencias potencialmente desastrosas (y ciertamente muy confusas).

(citó psmears'' comentario de psmears'' debido a su importancia, gracias por insinuarlo)


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 el tipo int deducido automáticamente:

auto x = some_function();

Así que es una extensión conservadora del lenguaje; encaja en la sintaxis existente. Sin él, x = some_function() convierte en una declaración de asignación, ya no en una declaración.