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 .
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 const
es 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;
x
int const
const
X : constant INTEGER := 1_
X
INTEGER
constant
Esto tiene dos resultados sutiles. En primer lugar, const
se 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 const
es 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 f
debe ser un número entero variable , pero i
es 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 const
a 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).
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.
Si bien una constante no cambia su valor mientras se ejecuta el programa, un objeto declarado const
puede 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 const
y 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
).
Además, una función miembro (no estática) se puede declarar como const
. En este caso, el this
puntero 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.
En C, C++ y D, se pueden declarar todos los tipos de datos, incluidos los definidos por el usuario, const
y 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 const
hace 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]
Para tipos de datos simples que no son punteros, aplicar el const
calificador 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 const
dos veces (por ejemplo, const char const
o char const const
) genera una advertencia pero no un error.
Para los tipos de puntero y referencia, el significado de const
es 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 const
puntero a un valor grabable, o un puntero grabable a un const
valor, o const
un puntero a const
un valor.Un const
puntero 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 const
los punteros. Un puntero a un const
objeto, 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 const
puntero a un const
objeto 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 }
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 *ptr
es un int
, mientras que la forma de referencia ptr
es un puntero a un int
. const
Modifica así el nombre a su derecha. En cambio, la convención de C++ es asociar *
con el tipo, como en int* ptr,
y leer const
como modificando el tipo a la izquierda. int const * ptrToConst
por lo tanto, se puede leer como " *ptrToConst
es un int const
" (el valor es constante) o " ptrToConst
es 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
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 * const
se 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í:
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 const
palabra 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.
const
se 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 const
variable 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.14159
parámetros realistas más largos o generales en tiempo de compilación. Una const
variable 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 const
parámetro en paso por referencia significa que el valor referenciado no se modifica – es parte del contrato – mientras que un const
pará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 const
parámetros in solo para el paso por referencia, donde cambia el contrato, pero no para el paso por valor, donde expone la implementación.
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 const
si no modificaran los datos de los miembros del objeto. Por lo tanto , aplicar el const
calificador 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.const
const
const
const
const
const
this
const
this
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 const
como un no const
mé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 const
cará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 const
referencia a an int
, en lugar de simplemente devolver el int
valor 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 ).
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 const
calificador, 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 const
mediante una conversión constante da como resultado un comportamiento indefinido según el estándar ISO C++. En el ejemplo anterior, si ptr
hace 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 LibraryFunc
realmente 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 const
o no, o si la variable de duración estática se declara const
o no. Existe una forma no portátil de inicializar una const
variable que tiene una duración de almacenamiento estática. Al construir cuidadosamente un encasillado en el lado izquierdo de una asignación posterior, const
se puede escribir en una variable, eliminando efectivamente el const
atributo e 'inicializándolo' con elementos no constantes como otras const
variables y demás. Escribir en una const
variable 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 const
propiedad de sus propietarios, es decir, un objeto contenedor que tiene const
todos const
los 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 s
al que se pasa Foo()
es constante, lo que hace que todos sus miembros sean constantes, la punta a la que se puede acceder s.ptr
sigue siendo modificable, aunque esto puede no ser deseable desde el punto de vista de const
la corrección porque s
podrí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 mutable
calificador 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 const
interfaz correcta, pero dichas clases no admiten la semántica de copia habitual de un const
objeto (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 const
la 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 const
puntero a una cadena de caracteres y devuelven un puntero const
a una parte de la misma cadena. strstr
y strchr
se 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 const
versión " " y una const
versión "no".
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 const
los calificadores de tipo de estilo, y en su lugar expresaron constancia mediante palabras clave que se aplican al identificador ( final
en Java const
y readonly
en C#). Incluso dentro de C y C++, el uso de const
varía significativamente: algunos proyectos y organizaciones lo utilizan de manera consistente y otros lo evitan.
El const
calificador 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 const
calificada), 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 strchr
función localiza un carácter en una cadena; formalmente, devuelve un puntero a la primera aparición del carácter c
en la cadena s
, y en C clásico (K&R C) su prototipo es:
char * strchr ( char * s , int c );
La strchr
funció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 const
el 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 const
si 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 const
tipo 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 inout
palabra 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. strchr
y las otras funciones afectadas por el problema devolverán un const
puntero si se les pasó uno y un puntero no calificado si se les pasó un puntero no calificado. [15]
En la versión 2 del lenguaje de programación D , existen dos palabras clave relacionadas con const. [16] La immutable
palabra clave denota datos que no pueden modificarse mediante ninguna referencia. La const
palabra clave denota una vista no mutable de datos mutables. A diferencia de C++ const
, D const
y immutable
son "profundos" o transitivos , y cualquier cosa accesible a través de un objeto const
o es o respectivamente.immutable
const
immutable
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).
const
Fue 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]
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 inline
palabra clave. * const
Dennis Ritchie sugirió los punteros constantes y la notación, que fueron adoptados. [18]
const
Luego 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 restrict
palabra 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ó const
de C++, donde se le conoce como constructor de tipos (no calificador de tipos ) y agregó dos constructores de tipos más, immutable
y inout
, para manejar casos de uso relacionados. [d]
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 const
palabra clave. Normalmente, esto sólo se utiliza para constantes (objetos constantes).
C# tiene una const
palabra 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 const
palabra 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 const
una 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 const
métodos de estilo C++ y puntero a const
tipo. [ cita necesaria ] Existe un ticket de solicitud de mejora para implementar const
la 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 constant
palabra clave, [24] [e] con parámetros de entrada y parámetros de bucle implícitamente constantes. Aquí constant
es una propiedad del objeto, no del tipo.
JavaScript tiene una const
declaració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]
const
es parte del tipo derivado más externo en una declaración; Los consejos complican la discusión.char *s
es estándar, mientras que en C++ char* s
es estándar.shared
constructor de tipos, pero esto está relacionado con casos de uso de volatile
, no de const
.El tipo de
en una función miembro cuyo tipo tiene un
cv-qualifier-seq cv
y cuya clase es
"puntero a
cv
".
this
X
X