stringtranslate.com

constante (programación informática)

En algunos lenguajes de programación , const es un calificador de tipo (una palabra clave aplicada a un tipo de datos ) que indica que los datos son de solo lectura. Si bien esto se puede usar para declarar constantes , const en la familia de lenguajes C se diferencia de construcciones similares en otros lenguajes en que es parte del tipo y, por lo tanto, tiene un comportamiento complicado cuando se combina con punteros , referencias, tipos de datos compuestos y tipos. -comprobación . En otros idiomas, los datos no están en una única ubicación de memoria , sino que se copian en el momento de la compilación en cada uso. [1] Los lenguajes que lo utilizan incluyen C , C++ , D , JavaScript , Julia y Rust .

Introducción

Cuando se aplica en una declaración de objeto , [a] indica que el objeto es una constante : su valor no puede cambiarse, a diferencia de una variable . Este uso básico (declarar constantes) tiene paralelos en muchos otros lenguajes.

Sin embargo, a diferencia de otros lenguajes, en la familia de lenguajes C constes parte del tipo , no parte del objeto . Por ejemplo, en C, declara un objeto de tipo – el es parte del tipo, como si fuera analizado "(int const) x" – mientras que en Ada , declara una constante (un tipo de objeto) de tipo: el es parte del objeto , pero no parte del tipo .int const x = 1;xint constconstX : constant INTEGER := 1_XINTEGERconstant

Esto tiene dos resultados sutiles. En primer lugar, constse puede aplicar a partes de un tipo más complejo: por ejemplo, int const * const x;declara un puntero constante a un entero constante, mientras int const * x;declara un puntero variable a un entero constante y int * const x;declara un puntero constante a un entero variable. En segundo lugar, como constes parte del tipo, debe coincidir como parte de la verificación de tipo. Por ejemplo, el siguiente código no es válido:

vacío f ( int & x ); // ... int const i ; f ( yo );    

porque el argumento fdebe ser un número entero variable , pero ies un número entero constante . Esta coincidencia es una forma de corrección del programa y se conoce como corrección constante . Esto permite una forma de programación por contrato , donde las funciones especifican como parte de su firma de tipo si modifican sus argumentos o no, y si su valor de retorno es modificable o no. Esta verificación de tipos es de interés principalmente en punteros y referencias (no en tipos de valores básicos como los enteros), sino también en tipos de datos compuestos o tipos con plantillas como contenedores . Está oculto por el hecho de que consta menudo se puede omitir, debido a la coerción de tipo ( conversión de tipo implícita ) y a que C es llamada por valor (C++ y D son llamada por valor o llamada por referencia).

Consecuencias

La idea de constancia no implica que la variable, tal como está almacenada en la memoria de la computadora , no pueda escribirse. Más bien, const-ness es una construcción en tiempo de compilación que indica lo que debe hacer un programador , no necesariamente lo que puede hacer. Tenga en cuenta, sin embargo, que en el caso de datos predefinidos (como char const * cadenas literales ), a menudoconst no se puede escribir en C.

Distinción de constantes

Si bien una constante no cambia su valor mientras se ejecuta el programa, un objeto declarado constpuede cambiar su valor mientras se ejecuta el programa. Un ejemplo común son los registros de solo lectura dentro de sistemas integrados, como el estado actual de una entrada digital. Los registros de datos para entradas digitales suelen declararse como consty volatile. El contenido de estos registros puede cambiar sin que el programa haga nada ( volatile) pero estaría mal formado que el programa intentara escribir en ellos ( const).

Otros usos

Además, una función miembro (no estática) se puede declarar como const. En este caso, el thispuntero dentro de dicha función es de tipo object_type const *en lugar de simplemente de tipo object_type *. [2] Esto significa que las funciones no constantes para este objeto no se pueden llamar desde dentro de dicha función, ni se pueden modificar las variables miembro . En C++, una variable miembro se puede declarar como mutable, lo que indica que esta restricción no se aplica a ella. En algunos casos, esto puede resultar útil, por ejemplo, con el almacenamiento en caché , el recuento de referencias y la sincronización de datos . En estos casos, el significado lógico (estado) del objeto no cambia, pero el objeto no es físicamente constante ya que su representación bit a bit puede cambiar.

Sintaxis

En C, C++ y D, se pueden declarar todos los tipos de datos, incluidos los definidos por el usuario, consty la corrección constante dicta que todas las variables u objetos deben declararse como tales a menos que sea necesario modificarlos. Este uso proactivo de consthace que los valores sean "más fáciles de entender, rastrear y razonar" [3] y, por lo tanto, aumenta la legibilidad y comprensibilidad del código y simplifica el trabajo en equipo y el mantenimiento del código porque comunica información sobre el uso previsto de un valor. Esto puede ayudar tanto al compilador como al desarrollador a razonar sobre el código. También puede permitir que un compilador de optimización genere código más eficiente. [4]

Tipos de datos simples

Para tipos de datos simples que no son punteros, aplicar el constcalificador es sencillo. Puede ir a ambos lados de algunos tipos por razones históricas (por ejemplo, const char foo = 'a';equivale a char const foo = 'a';). En algunas implementaciones, usar constdos veces (por ejemplo, const char consto char const const) genera una advertencia pero no un error.

Consejos y referencias

Para los tipos de puntero y referencia, el significado de constes más complicado: el puntero en sí, el valor al que apunta, o ambos, pueden ser const. Además, la sintaxis puede resultar confusa. Un puntero se puede declarar como un constpuntero a un valor grabable, o un puntero grabable a un constvalor, o constun puntero a constun valor.Un constpuntero no se puede reasignar para que apunte a un objeto diferente al que se le asignó inicialmente, pero se puede utilizar para modificar el valor al que apunta (llamado puntero ) . [5] [6] [7] [8] [9] Las variables de referencia en C++ son una sintaxis alternativa para constlos punteros. Un puntero a un constobjeto, por otro lado, se puede reasignar para que apunte a otra ubicación de memoria (que debería ser un objeto del mismo tipo o de tipo convertible), pero no se puede utilizar para modificar la memoria a la que apunta. a. También se puede declarar un constpuntero a un constobjeto y no se puede utilizar para modificar al designado ni reasignarlo para que apunte a otro objeto. El siguiente código ilustra estas sutilezas:

void Foo ( int * ptr , int const * ptrToConst , int * const constPtr , int const * const constPtrToConst ) { * ptr = 0 ; // OK: modifica los datos apuntados ptr = NULL ; // OK: modifica el puntero                           * ptrToConst = 0 ; // ¡Error! No se pueden modificar los datos apuntados ptrToConst = NULL ; // OK: modifica el puntero        * punto constante = 0 ; // OK: modifica los datos apuntados constPtr = NULL ; // ¡Error! No se puede modificar el puntero        * constPtrToConst = 0 ; // ¡Error! No se pueden modificar los datos señalados constPtrToConst = NULL ; // ¡Error! No se puede modificar el puntero }       

convención C

Siguiendo la convención habitual de C para declaraciones, la declaración sigue al uso y el *puntero in se escribe en el puntero, lo que indica desreferenciación . Por ejemplo, en la declaración int *ptr, la forma desreferenciada *ptres un int, mientras que la forma de referencia ptres un puntero a un int. constModifica así el nombre a su derecha. En cambio, la convención de C++ es asociar *con el tipo, como en int* ptr,y leer constcomo modificando el tipo a la izquierda. int const * ptrToConstpor lo tanto, se puede leer como " *ptrToConstes un int const" (el valor es constante) o " ptrToConstes un int const *" (el puntero es un puntero a un número entero constante). De este modo:

int * ptr ; // *ptr es un valor int int const * ptrToConst ; // *ptrToConst es una constante (int: valor entero) int * const constPtr ; // constPtr es una constante (int *: puntero entero) int const * const constPtrToConst ; // constPtrToConst es un puntero constante y apunta // a un valor constante               

convención C ++

Siguiendo la convención de C++ de analizar el tipo, no el valor, una regla general es leer la declaración de derecha a izquierda. Por lo tanto, todo lo que está a la izquierda de la estrella puede identificarse como el tipo puntiagudo y todo lo que está a la derecha de la estrella son las propiedades del puntero. Por ejemplo, en nuestro ejemplo anterior, int const *se puede leer como un puntero grabable que hace referencia a un entero no grabable y int * constse puede leer como un puntero no grabable que hace referencia a un entero grabable.

Una regla más genérica que le ayuda a comprender declaraciones y definiciones complejas funciona así:

  1. encuentre el identificador cuya declaración desea comprender
  2. leer lo más posible hacia la derecha (es decir, hasta el final de la declaración o hasta el siguiente paréntesis de cierre, lo que ocurra primero)
  3. retroceda hasta donde comenzó y lea hacia atrás hacia la izquierda (es decir, hasta el comienzo de la declaración o hasta el paréntesis de apertura que coincide con el paréntesis de cierre que se encuentra en el paso anterior)
  4. cuando haya llegado al comienzo de la declaración, habrá terminado. De lo contrario, continúe en el paso 2, más allá del paréntesis de cierre que coincidió en último lugar.

Aquí hay un ejemplo:

Al leer hacia la izquierda, es importante que leas los elementos de derecha a izquierda. Entonces an int const *se convierte en un puntero a const int y no en un puntero constante a int .

En algunos casos, C/C++ permite que la constpalabra clave se coloque a la izquierda del tipo. Aquí hay unos ejemplos:

const int * ptrToConst ; //idéntico a: int const *ptrToConst, const int * const constPtrToConst ; //idéntico a: int const *const constPtrToConst       

Aunque C/C++ permite este tipo de definiciones (que se asemejan mucho al idioma inglés cuando se leen las definiciones de izquierda a derecha), el compilador aún lee las definiciones de acuerdo con el procedimiento mencionado anteriormente: de derecha a izquierda. Pero const anteponer lo que debe ser constante rápidamente introduce discrepancias entre lo que pretendes escribir y lo que el compilador decide que escribiste. Considere sugerencias a sugerencias:

int ** ptr ; // un puntero a un puntero a ints int const ** ptr // un puntero a un puntero a un valor int constante // (no un puntero a un puntero constante a ints) int * const * ptr // un puntero a una const puntero a valores int // (no es un puntero constante a un puntero a ints) int ** const ptr // un puntero constante a punteros a ints // (ptr, el identificador, ser constante no tiene sentido) int const ** const ptr // un puntero constante a punteros a valores int constantes                  

Como nota final con respecto a las definiciones de puntero: escriba siempre el símbolo del puntero (el *) tanto como sea posible a la derecha. Adjuntar el símbolo de puntero al tipo es complicado, ya que sugiere claramente un tipo de puntero, lo cual no es el caso. Aquí hay unos ejemplos:

int * a ; /* escribir: */ int * a ; // a es un puntero a un int int * a , b ; // CONFUSO /* escribir: */ int * a , b ; // a es un puntero a un int, // pero b es un simple int int * a , * b ; // FEO: tanto a como b son punteros a ints /* escribe: */ int * a , * b ;                     

Las preguntas frecuentes de Bjarne Stroustrup recomiendan declarar solo una variable por línea si se usa la convención C++, para evitar este problema. [10]

Las mismas consideraciones se aplican a la definición de referencias y referencias de valores:

int var = 22 ; int const & refToConst = var ; // OK int const & ref2 = var , ref3 = var ; // CONFUSOR: // ref2 es una referencia, pero ref3 no lo es: // ref3 es una constante int inicializada con // el valor de var int & const constRef = var ; // ERROR: ya que las referencias no pueden cambiar de todos modos.                        // C++: int && rref = int ( 5 ), valor = 10 ; // CONFUSO: // rref es una referencia de rvalue, pero el valor es // un mero int. /* escribir: */ int && rref = int ( 5 ), valor = 10 ;                 

Se encuentran declaraciones más complicadas cuando se utilizan matrices multidimensionales y referencias (o punteros) a punteros. Aunque a veces se discute ¿ quién? ] que tales declaraciones son confusas y propensas a errores y que, por lo tanto, deben evitarse o reemplazarse por estructuras de nivel superior, el procedimiento descrito al principio de esta sección siempre se puede utilizar sin introducir ambigüedades o confusión.

Parámetros y variables

constse puede declarar tanto en parámetros de función como en variables ( estática o automática, incluso global o local). La interpretación varía según los usos. Una constvariable estática (variable global o variable local estática) es una constante y puede usarse para datos como constantes matemáticas, como double const PI = 3.14159parámetros realistas más largos o generales en tiempo de compilación. Una constvariable automática (variable local no estática) significa que se está realizando una asignación única , aunque se puede usar un valor diferente cada vez, como por ejemplo int const x_squared = x * x. Un constparámetro en paso por referencia significa que el valor referenciado no se modifica – es parte del contrato – mientras que un constparámetro en paso por valor (o el puntero mismo, en paso por referencia) no agrega nada a la interfaz (ya que el valor ha sido copiado), pero indica que internamente la función no modifica la copia local del parámetro (es una asignación única). Por esta razón, algunos prefieren usar constparámetros in solo para el paso por referencia, donde cambia el contrato, pero no para el paso por valor, donde expone la implementación.

C++

Métodos

Para aprovechar el enfoque de diseño por contrato para tipos definidos por el usuario (estructuras y clases), que pueden tener métodos además de datos de miembros, el programador puede etiquetar métodos de instancia como constsi no modificaran los datos de los miembros del objeto. Por lo tanto , aplicar el constcalificador a métodos de instancia es una característica esencial para la corrección constante y no está disponible en muchos otros lenguajes orientados a objetos como Java y C# o en C++/CLI o Managed Extensions for C++ de Microsoft . Mientras que los métodos pueden ser invocados por objetos y no objetos por igual, los no métodos sólo pueden ser invocados por no objetos. El modificador de un método de instancia se aplica al objeto señalado por el puntero " ", que es un argumento implícito que se pasa a todos los métodos de instancia. Por lo tanto, tener métodos es una forma de aplicar la corrección constante al " " argumento del puntero implícito al igual que otros argumentos.constconstconstconstconstconstthisconstthis

Este ejemplo ilustra:

clase C { int i ; public : int Get () const // Tenga en cuenta la etiqueta "const" { return i ; } void Set ( int j ) // Tenga en cuenta la falta de "const" { i = j ; } };                    void Foo ( C y noConstC , C const y constC ) { int y = noConstC . Conseguir (); // Ok int x = constC . Conseguir (); // Ok: Get() es constante                noConstC . Establecer ( 10 ); // Ok: nonConstC es modificable constC . Establecer ( 10 ); // ¡Error! Set() es un método no constante y constC es un objeto calificado de manera constante }   

En el código anterior, el " this" puntero implícito Set()tiene el tipo " C *const"; mientras que el " this" puntero Get()tiene tipo " C const *const", lo que indica que el método no puede modificar su objeto a través del " this" puntero.

A menudo, el programador proporcionará tanto un método constcomo un no constmétodo con el mismo nombre (pero posiblemente con usos bastante diferentes) en una clase para dar cabida a ambos tipos de llamantes. Considerar:

clase MiArray { int datos [ 100 ]; público : int & Get ( int i ) { datos de retorno [ i ]; } int const & Obtener ( int i ) const { devolver datos [ i ]; } };                     void Foo ( MyArray & array , MyArray const & constArray ) { // Obtener una referencia a un elemento de la matriz // y modificar su valor referenciado.            matriz . Obtener ( 5 ) = 42 ; // ¡DE ACUERDO! (Llamadas: int & MyArray::Get(int)) constArray . Obtener ( 5 ) = 42 ; // ¡Error! (Llamadas: int const & MyArray::Get(int) const) }           

El constcarácter del objeto que llama determina qué versión de MyArray::Get()se invocará y, por lo tanto, si al llamador se le da o no una referencia con la que puede manipular o solo observar los datos privados en el objeto. Los dos métodos técnicamente tienen firmas diferentes porque sus " this" punteros tienen tipos diferentes, lo que permite al compilador elegir el correcto. (Devolver una constreferencia a an int, en lugar de simplemente devolver el intvalor by, puede ser excesivo en el segundo método, pero se puede usar la misma técnica para tipos arbitrarios, como en la Biblioteca de plantillas estándar ).

Lagunas para la corrección constante

Hay varias lagunas en la corrección constante pura en C y C++. Existen principalmente por compatibilidad con el código existente.

El primero, que se aplica sólo a C++, es el uso de const_cast, que permite al programador eliminar el constcalificador, haciendo que cualquier objeto sea modificable. La necesidad de eliminar el calificador surge cuando se utilizan código y bibliotecas existentes que no se pueden modificar pero que no son constantes y correctas. Por ejemplo, considere este código:

// Prototipo de una función que no podemos cambiar pero que // sabemos que no modifica la punta pasada. void LibraryFunc ( int * ptr , int size );    void CallLibraryFunc ( int const * ptr , int tamaño ) { LibraryFunc ( ptr , tamaño ); // ¡Error! Elimina el clasificatorio constante          int * noConstPtr = const_cast < int *> ( ptr ); // Elimina el calificador LibraryFunc ( nonConstPtr , tamaño ); // DE ACUERDO }       

Sin embargo, cualquier intento de modificar un objeto que está declarado constmediante una conversión constante da como resultado un comportamiento indefinido según el estándar ISO C++. En el ejemplo anterior, si ptrhace referencia a una variable global, local o miembro declarada como const, o a un objeto asignado en el montón mediante new int const, el código solo es correcto si LibraryFuncrealmente no modifica el valor señalado por ptr.

El lenguaje C necesita una laguna jurídica porque existe una determinada situación. Las variables con duración de almacenamiento estático pueden definirse con un valor inicial. Sin embargo, el inicializador solo puede usar constantes como constantes de cadena y otros literales, y no se le permite usar elementos no constantes como nombres de variables, ya sea que los elementos del inicializador estén declarados consto no, o si la variable de duración estática se declara consto no. Existe una forma no portátil de inicializar una constvariable que tiene una duración de almacenamiento estática. Al construir cuidadosamente un encasillado en el lado izquierdo de una asignación posterior, constse puede escribir en una variable, eliminando efectivamente el constatributo e 'inicializándolo' con elementos no constantes como otras constvariables y demás. Escribir en una constvariable de esta manera puede funcionar según lo previsto, pero causa un comportamiento indefinido y contradice seriamente la corrección constante:

size_t const bufferSize = 8 * 1024 ; size_t const usuarioTextBufferSize ; //el valor inicial depende del tamaño del buffer constante, no se puede inicializar aquí       ...int setupUserTextBox ( textBox_t * defaultTextBoxType , rect_t * defaultTextBoxLocation ) { * ( size_t * ) & userTextBufferSize = bufferSize - sizeof ( struct textBoxControls ); // advertencia: podría funcionar, pero C no lo garantiza ... }            

Otra laguna jurídica [11] se aplica tanto a C como a C++. Específicamente, los lenguajes dictan que los punteros y referencias de miembros son "superficiales" con respecto a la constpropiedad de sus propietarios, es decir, un objeto contenedor que tiene consttodos constlos miembros, excepto los miembros señalados (y árbitros), que aún son mutables. Para ilustrar, considere este código C++:

estructura S { int valor ; int * ptr ; };     vacío Foo ( S const & s ) { int i = 42 ; s . valor = yo ; // Error: s es constante, por lo que val es constante int s . ptr = & yo ; // Error: s es constante, por lo que ptr es un puntero constante a int * s . ptr = yo ; // OK: los datos señalados por ptr siempre son mutables, // aunque esto a veces no es deseable }                     

Aunque el objeto sal que se pasa Foo()es constante, lo que hace que todos sus miembros sean constantes, la punta a la que se puede acceder s.ptrsigue siendo modificable, aunque esto puede no ser deseable desde el punto de vista de constla corrección porque spodría ser la única que posea la punta. Por esta razón, Meyers sostiene que el valor predeterminado para los punteros y referencias de miembros debería ser "profundo" const, que podría ser anulado por un mutablecalificador cuando la punta no es propiedad del contenedor, pero esta estrategia crearía problemas de compatibilidad con el código existente. Por lo tanto, por razones históricas [ cita necesaria ] , esta laguna permanece abierta en C y C++.

Esta última laguna se puede cerrar usando una clase para ocultar el puntero detrás de una constinterfaz correcta, pero dichas clases no admiten la semántica de copia habitual de un constobjeto (lo que implica que la clase contenedora tampoco puede copiarse mediante la semántica habitual) o permitir otras lagunas al permitir la eliminación de constla identidad mediante copias inadvertidas o intencionales.

Finalmente, varias funciones en la biblioteca estándar de C violan la corrección constante antes de C23 , ya que aceptan un constpuntero a una cadena de caracteres y devuelven un puntero consta una parte de la misma cadena. strstry strchrse encuentran entre estas funciones. Algunas implementaciones de la biblioteca estándar de C++, como la de Microsoft [12], intentan cerrar esta laguna proporcionando dos versiones sobrecargadas de algunas funciones: una constversión " " y una constversión "no".

Problemas

El uso del sistema de tipos para expresar constancia genera diversas complejidades y problemas y, en consecuencia, ha sido criticado y no adoptado fuera de la estrecha familia C de C, C++ y D. Java y C#, que están fuertemente influenciados por C y C++, ambos rechazaron explícitamente constlos calificadores de tipo de estilo, y en su lugar expresaron constancia mediante palabras clave que se aplican al identificador ( finalen Java consty readonlyen C#). Incluso dentro de C y C++, el uso de constvaría significativamente: algunos proyectos y organizaciones lo utilizan de manera consistente y otros lo evitan.

strchrproblema

El constcalificador de tipo causa dificultades cuando la lógica de una función es independiente de si su entrada es constante o no, pero devuelve un valor que debe ser del mismo tipo calificado que la entrada. En otras palabras, para estas funciones, si la entrada es constante (calificada constantemente), el valor de retorno también debería serlo, pero si la entrada es variable (no constcalificada), el valor de retorno también debería serlo. Debido a que el tipo de firma de estas funciones difiere, se requieren dos funciones (o potencialmente más, en el caso de múltiples entradas) con la misma lógica: una forma de programación genérica .

Este problema surge incluso para funciones simples en la biblioteca estándar de C, en particular strchr; Ritchie atribuye esta observación a Tom Plum a mediados de la década de 1980. [13] La strchrfunción localiza un carácter en una cadena; formalmente, devuelve un puntero a la primera aparición del carácter cen la cadena s, y en C clásico (K&R C) su prototipo es:

char * strchr ( char * s , int c );    

La strchrfunción no modifica la cadena de entrada, pero la persona que llama suele utilizar el valor de retorno para modificar la cadena, como por ejemplo:

si ( p = strchr ( q , '/' )) * p = '' ;       

Así, por un lado, la cadena de entrada puede ser const(ya que la función no la modifica), y si la cadena de entrada es constel valor de retorno también debería serlo, simplemente porque podría devolver exactamente el puntero de entrada, si el primer carácter es una coincidencia, pero por otro lado el valor de retorno no debería serlo constsi la cadena original no lo era const, ya que la persona que llama puede querer usar el puntero para modificar la cadena original.

En C++, esto se hace mediante sobrecarga de funciones , generalmente implementada mediante una plantilla , lo que da como resultado dos funciones, de modo que el valor de retorno tiene el mismo consttipo calificado que la entrada: [b]

char * strchr ( char * s , int c ); char const * strchr ( char const * s , int c );          

Estos, a su vez, pueden definirse mediante una plantilla:

plantilla < T > T * strchr ( T * s , int c ) { ... }        

En D, esto se maneja mediante la inoutpalabra clave, que actúa como comodín para constante, inmutable o no calificado (variable), lo que produce: [14] [c]

entrada ( char )* strchr ( entrada ( char )* s , int c );    

Sin embargo, en C nada de esto es posible ya que C no tiene sobrecarga de funciones y, en cambio, esto se maneja teniendo una única función donde la entrada es constante pero la salida se puede escribir:

char * strchr ( char const * s , int c );     

Esto permite código C idiomático pero elimina el calificador const si la entrada realmente estaba calificada const, violando la seguridad de tipos. Esta solución fue propuesta por Ritchie y posteriormente adoptada. Esta diferencia es uno de los fallos de compatibilidad de C y C++ .

Desde C23 , este problema se soluciona con el uso de funciones genéricas. strchry las otras funciones afectadas por el problema devolverán un constpuntero si se les pasó uno y un puntero no calificado si se les pasó un puntero no calificado. [15]

D

En la versión 2 del lenguaje de programación D , existen dos palabras clave relacionadas con const. [16] La immutablepalabra clave denota datos que no pueden modificarse mediante ninguna referencia. La constpalabra clave denota una vista no mutable de datos mutables. A diferencia de C++ const, D consty immutableson "profundos" o transitivos , y cualquier cosa accesible a través de un objeto consto es o respectivamente.immutableconstimmutable

Ejemplo de constante versus inmutable en D

int [] foo = nuevo int [ 5 ]; // foo es mutable. const int [] barra = foo ; // bar es una vista constante de datos mutables. inmutable int [] baz = foo ; // Error: todas las vistas de datos inmutables deben ser inmutables.               inmutable int [] nums = nuevo inmutable ( int ) [ 5 ]; // No se puede crear ninguna referencia mutable a nums. const int [] constNums = números ; // Obras. inmutable es implícitamente convertible a const. int [] números mutables = números ; // Error: no se puede crear una vista mutable de datos inmutables.               

Ejemplo de constante transitiva o profunda en D

clase Foo { Foo siguiente ; número int ; }      inmutable Foo foo = nuevo inmutable ( Foo ); foo . próximo . número = 5 ; // No se compilará. foo.next es de tipo inmutable (Foo). // foo.next.num es de tipo inmutable(int).         

Historia

constFue introducido por Bjarne Stroustrup en C con Classes , el predecesor de C++ , en 1981, y originalmente se llamó readonly. [17] [18] En cuanto a la motivación, Stroustrup escribe: [18]

"Cumplió dos funciones: como una forma de definir una constante simbólica que obedece a reglas de alcance y tipo (es decir, sin usar una macro) y como una forma de considerar un objeto en la memoria inmutable".

El primer uso, como alternativa a las macros con alcance y tipo, se cumplió de manera análoga para macros similares a funciones a través de la inlinepalabra clave. * constDennis Ritchie sugirió los punteros constantes y la notación, que fueron adoptados. [18]

constLuego se adoptó en C como parte de la estandarización y aparece en C89 (y versiones posteriores) junto con el otro calificador de tipo, volatile. [19]noalias En la reunión de diciembre de 1987 del comité X3J11 se sugirió otro calificativo , pero fue rechazado; su objetivo finalmente se cumplió con la restrictpalabra clave en C99 . Ritchie no apoyó mucho estas adiciones, argumentando que no "llevaban su peso", pero en última instancia no defendió su eliminación de la norma. [20]

Posteriormente, D heredó constde C++, donde se le conoce como constructor de tipos (no calificador de tipos ) y agregó dos constructores de tipos más, immutabley inout, para manejar casos de uso relacionados. [d]

Otros idiomas

Otros lenguajes no siguen a C/C++ en cuanto a tener constancia como parte del tipo, aunque a menudo tienen construcciones superficialmente similares y pueden usar la constpalabra clave. Normalmente, esto sólo se utiliza para constantes (objetos constantes).

C# tiene una constpalabra clave, pero con una semántica radicalmente diferente y más simple: significa una constante en tiempo de compilación y no es parte del tipo.

Nim tiene una constpalabra clave similar a la de C#: también declara una constante de tiempo de compilación en lugar de formar parte del tipo. Sin embargo, en Nim, se puede declarar una constante a partir de cualquier expresión que pueda evaluarse en tiempo de compilación. [21] En C#, sólo los tipos integrados de C# se pueden declarar como const; Los tipos definidos por el usuario, incluidas clases, estructuras y matrices, no pueden ser const. [22]

Java no tiene const, sino que tiene final, que se puede aplicar a declaraciones de "variables" locales y se aplica al identificador, no al tipo. Tiene un uso diferente orientado a objetos para los miembros de objetos, que es el origen del nombre.

La especificación del lenguaje Java considera constuna palabra clave reservada, es decir, una que no puede usarse como identificador de variable, pero no le asigna ninguna semántica: es una palabra reservada (no puede usarse en identificadores) pero no una palabra clave (no tiene ningún significado especial). significado). Se cree que la reserva de la palabra clave se produjo para permitir una extensión del lenguaje Java para incluir constmétodos de estilo C++ y puntero a consttipo. [ cita necesaria ] Existe un ticket de solicitud de mejora para implementar constla corrección en el Proceso de la comunidad Java , pero se cerró en 2005 porque era imposible implementarlo de manera compatible con versiones anteriores. [23]

El contemporáneo Ada 83 tenía de forma independiente la noción de un objeto constante y una constantpalabra clave, [24] [e] con parámetros de entrada y parámetros de bucle implícitamente constantes. Aquí constantes una propiedad del objeto, no del tipo.

JavaScript tiene una constdeclaración que define una variable con ámbito de bloque que no se puede reasignar ni redeclarar. Define una referencia de solo lectura a una variable que no se puede redefinir, pero en algunas situaciones el valor de la variable en sí puede cambiar, como si la variable se refiere a un objeto y se modifica una propiedad del mismo. [25]

Ver también

Notas

  1. ^ Formalmente cuando constes parte del tipo derivado más externo en una declaración; Los consejos complican la discusión.
  2. ^ Tenga en cuenta que las convenciones de sintaxis de declaración de punteros difieren entre C y C++: en C char *ses estándar, mientras que en C++ char* ses estándar.
  3. ^ El código D idiomático usaría una matriz aquí en lugar de un puntero. [14]
  4. ^ D también introdujo el sharedconstructor de tipos, pero esto está relacionado con casos de uso de volatile, no de const.
  5. ^ El estándar Ada llama a esto una " palabra reservada "; consulte ese artículo para conocer su uso.

Referencias

  1. ^ "Elementos constantes: la referencia de Rust". doc.rust-lang.org . Consultado el 22 de junio de 2022 .
  2. ^ "El puntero este". Borrador del estándar C++ . Consultado el 30 de marzo de 2020 . El tipo de en una función miembro cuyo tipo tiene un cv-qualifier-seq cv y cuya clase es "puntero a cv ".thisX X
  3. ^ Herb Sutter y Andrei Alexandrescu (2005). Estándares de codificación C++ . pag. 30. Boston: Addison Wesley. ISBN 0-321-11358-6 
  4. ^ "¿Por qué el argumento kfree() es constante?". lkml.org. 2013-01-12.
  5. ^ "5.1. Extensiones implementadas en GNU Fortran: 5.1.16 punteros Cray". El compilador GNU Fortran. 2006. Archivado desde el original el 21 de diciembre de 2022 . Consultado el 21 de diciembre de 2022 .
  6. ^ Fahey, Mark R.; Nagle, Dan (19 de abril de 1999). "Cray Fortran Pointers frente a Fortran 90 Pointers y transferencia de Cray C90 a SGI Origin2000" (PDF) . Vicksburg, Massachusetts, EE. UU.: Estación experimental de vías navegables del Cuerpo de Ingenieros del Ejército de EE. UU., Centro principal de recursos compartidos. Archivado (PDF) desde el original el 23 de diciembre de 2022 . Consultado el 23 de diciembre de 2022 .(8 páginas)
  7. ^ "Apéndice C: Características y diferencias de Fortran 90 > Características > Punteros de Cray". Guía del usuario de Fortran . Corporación Oráculo . 2010. Archivado desde el original el 21 de septiembre de 2021 . Consultado el 23 de diciembre de 2022 .
  8. ^ "Apéndice C: Características y diferencias de Fortran 90 > Características > Punteros de caracteres de Cray". Guía del usuario de Fortran . Corporación Oráculo . 2010. Archivado desde el original el 23 de diciembre de 2022 . Consultado el 23 de diciembre de 2022 .
  9. ^ "Capítulo 4. Tipos de datos". Manual de referencia del lenguaje Fortran, volumen 1. Vol. 1. Silicon Graphics, Inc. 1999 [1993]. Número de documento: 007-3692-004. Archivado desde el original el 23 de diciembre de 2022 . Consultado el 23 de diciembre de 2022 .(NB. Derivado del "FORTRAN 90 HANDBOOK" (1992, McGraw-Hill, Inc. ) de Walter S. Brainerd, Jeanne C. Adams, Jeanne T. Martin, Brian T. Smith y Jerrold L. Wagener.)
  10. ^ "Stroustrup: preguntas frecuentes sobre técnicas y estilos de C++".
  11. ^ Scott Meyers (2005). C++ efectivo, tercera edición . págs. 21-23. Boston: Addison Wesley. ISBN 978-0-321-33487-9 
  12. ^ "strchr, wcschr, _mbschr (CRT)". msdn.microsoft.com . Consultado el 23 de noviembre de 2017 .
  13. ^ "Dennis Ritchie: Por qué no me gustan los calificadores tipo X3J11".
  14. ^ ab El lenguaje de programación D, Andrei Alexandrescu , 8.8: propagación de un calificador del parámetro al resultado
  15. ^ "WG14-N3020: funciones de biblioteca estándar que preservan el calificador" (PDF) . open-std.org . 2022-06-13. Archivado (PDF) desde el original el 13 de octubre de 2022.
  16. ^ "const (Preguntas frecuentes) - Lenguaje de programación D". Digitalmars.com . Consultado el 18 de agosto de 2013 .
  17. ^ Bjarne Stroustrup , "Extensiones del concepto de tipo de lenguaje C", Memorando técnico interno de Bell Labs, 5 de enero de 1981.
  18. ^ abc Rivalidad entre hermanos: C y C++, Bjarne Stroustrup , 2002, pág. 5
  19. ^ Dennis M. Ritchie , "El desarrollo del lenguaje C Archivado el 15 de julio de 2012 en archive.today ", 2003: "X3J11 también introdujo una serie de adiciones y ajustes más pequeños, por ejemplo, los calificadores de tipo const y volatile , y reglas de promoción de tipo ligeramente diferentes."
  20. ^ "Permítanme comenzar diciendo que no estoy convencido de que incluso los calificadores anteriores a diciembre ('const' y 'volatile') tengan su peso; sospecho que lo que agregan al costo de aprender y usar el idioma no es recompensado con una mayor expresividad. "Volatile", en particular, es un adorno para aplicaciones esotéricas, y se expresa mucho mejor por otros medios. Su principal virtud es que casi todo el mundo puede olvidarse de él. "Const" es a la vez más útil y más llamativo; no puedes evitar aprender sobre él, debido a su presencia en la interfaz de la biblioteca. Sin embargo, no estoy a favor de la eliminación de los calificadores, aunque sólo sea porque ya es demasiado tarde."
  21. ^ Manual de Nim: sección constante
  22. ^ constante (referencia de C#)
  23. ^ "ID de error: JDK-4211070 Java debería admitir parámetros constantes (como C++) para el mantenimiento del código [sic]". Bugs.sun.com . Consultado el 4 de noviembre de 2014 .
  24. ^ 1815A [ enlace muerto ] , 3.2.1. Declaraciones de objetos Archivadas el 20 de octubre de 2014 en Wayback Machine :
    "El objeto declarado es una constante si la palabra reservada constante aparece en la declaración del objeto; la declaración debe incluir una inicialización explícita. El valor de una constante no se puede modificar después de la inicialización . Los parámetros formales de modo de entrada de subprogramas y entradas, y los parámetros formales genéricos de modo de entrada, también son constantes; un parámetro de bucle es una constante dentro del bucle correspondiente; un subcomponente o porción de una constante es una constante."
  25. ^ "constante". MDN . Consultado el 31 de octubre de 2017 .

enlaces externos