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 difiere 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 verificación de tipos . En otros lenguajes, los datos no están en una única ubicación de memoria , sino que se copian en tiempo de compilación para cada uso. [1] Los lenguajes que lo usan 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 modificarse, 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, la const
es parte del tipo , no parte del objeto . Por ejemplo, en C, declara un objeto de tipo – la es parte del tipo, como si se analizara "(int const) x" – mientras que en Ada , declara una constante (una especie de objeto) de tipo: la 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 que 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, debido a que const
es parte del tipo, debe coincidir como parte de la verificación de tipos. Por ejemplo, el siguiente código no es válido:
vacío f ( int & x ); // ... int const i ; f ( i );
porque el argumento de f
debe ser un entero variablei
, pero es un entero constante . Esta coincidencia es una forma de corrección del programa , y se conoce como const-correctness . 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 comprobación de tipos es principalmente de interés en punteros y referencias, no tipos de valor básico como enteros, sino también para tipos de datos compuestos o tipos con plantilla como contenedores . Está oculto por el hecho de que a const
menudo se puede omitir, debido a la coerción de tipos ( conversión de tipos implícita ) y C es de llamada por valor (C++ y D son de llamada por valor o de llamada por referencia).
La idea de la constancia no implica que la variable tal como está almacenada en la memoria de la computadora no se pueda escribir. Más bien, const
la constancia es una construcción en tiempo de compilación que indica lo que un programador debe hacer, no necesariamente lo que puede hacer. Sin embargo, tenga en cuenta que en el caso de datos predefinidos (como char const *
literales de cadena ), C const
a menudo no se puede escribir.
Si bien una constante no cambia su valor mientras el programa se está ejecutando, un objeto declarado const
puede cambiar su valor mientras el programa se está ejecutando. Un ejemplo común son los registros de solo lectura dentro de los sistemas integrados, como el estado actual de una entrada digital. Los registros de datos para las entradas digitales a menudo se declaran como const
y volatile
. El contenido de estos registros puede cambiar sin que el programa haga nada ( volatile
), pero sería incorrecto que el programa intentara escribir en ellos ( const
).
Además, una función miembro (no estática) puede declararse 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 pueden llamarse desde dentro de dicha función, ni pueden modificarse las variables miembro . En C++, una variable miembro puede declararse como mutable
, lo que indica que esta restricción no se le aplica. En algunos casos, esto puede ser útil, por ejemplo, con el almacenamiento en caché , el conteo 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, todos los tipos de datos, incluidos los definidos por el usuario, pueden declararse 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 la comprensión del código y hace que trabajar en equipo y mantener el código sea más simple porque comunica información sobre el uso previsto de un valor. Esto puede ayudar tanto al compilador como al desarrollador al razonar sobre el código. También puede permitir que un compilador optimizador genere código más eficiente. [4]
Para los tipos de datos simples que no son punteros, la aplicación del const
calificador es sencilla. Puede ir en cualquier lado de algunos tipos por razones históricas (por ejemplo, const char foo = 'a';
es equivalente a char const foo = 'a';
). En algunas implementaciones, el uso const
dos veces (por ejemplo, const char const
o char const const
) genera una advertencia, pero no un error.
En el caso de los tipos de puntero y de referencia, el significado de const
es más complicado: el puntero en sí, el valor al que se apunta o ambos pueden ser const
. Además, la sintaxis puede ser confusa. Un puntero se puede declarar como un const
puntero a un valor escribible, un puntero escribible a un const
valor o const
un puntero a const
un valor.Un const
puntero no puede reasignarse para que apunte a un objeto diferente del que se le asignó inicialmente, pero puede usarse 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. const
Por otro lado, un puntero a un objeto puede reasignarse para que apunte a otra ubicación de memoria (que debería ser un objeto del mismo tipo o de un tipo convertible), pero no puede usarse para modificar la memoria a la que apunta. Un const
puntero a un const
objeto también puede declararse y no puede usarse para modificar el puntero ni reasignarse 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 * constPtr = 0 ; // OK: modifica los datos apuntados constPtr = NULL ; // ¡Error! No se puede modificar el puntero * constPtrToConst = 0 ; // ¡Error! No se pueden modificar los datos apuntados 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 *
en un puntero se escribe en el puntero, lo que indica que se desreferencia . 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
. Por lo tanto, const
modifica el nombre a su derecha. La convención de C++ es, en cambio, asociar el *
con el tipo, como en int* ptr
, y leer el 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 entero constante). Por lo tanto:
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 se encuentra a la izquierda de la estrella se puede identificar como el tipo puntiagudo y todo lo que se encuentra 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 escribible que hace referencia a un entero no escribible, y int * const
se puede leer como un puntero no escribible que hace referencia a un entero escribible.
Una regla más genérica que te ayuda a comprender declaraciones y definiciones complejas funciona así:
He aquí un ejemplo:
Al leer hacia la izquierda, es importante que lea los elementos de derecha a izquierda. De esta manera, an int const *
se convierte en un puntero a un int constante y no en un puntero constante a un int .
En algunos casos, C/C++ permite const
colocar la palabra clave a la izquierda del tipo. A continuación, se muestran algunos 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 parecen 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 poner const
antes de lo que debe ser constante introduce rápidamente desajustes entre lo que pretende escribir y lo que el compilador decide que escribió. Considere los punteros a punteros:
int ** ptr ; // un puntero a un puntero a int int const ** ptr // un puntero a un puntero a un valor int constante // (no un puntero a un puntero constante a int) int * const * ptr // un puntero a un puntero constante a valores int // (no un puntero constante a un puntero a int) int ** const ptr // un puntero constante a punteros a int // (ptr, el identificador, al ser constante no tiene sentido) int const ** const ptr // un puntero constante a punteros a valores int constantes
Como nota final sobre las definiciones de punteros: siempre escriba el símbolo del puntero (el *) lo más a la derecha posible. Adjuntar el símbolo del puntero al tipo es complicado, ya que sugiere claramente un tipo de puntero, lo que no es el caso. A continuación se muestran algunos 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 int /* escribir: */ int * a , * b ;
Las preguntas frecuentes de Bjarne Stroustrup recomiendan declarar solo una variable por línea si se utiliza la convención C++, para evitar este problema. [10]
Las mismas consideraciones se aplican a la definición de referencias y referencias de valores r:
int var = 22 ; int const & refToConst = var ; // OK int const & ref2 = var , ref3 = var ; // CONFUSO: // 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: las referencias no pueden cambiar de todos modos. // C++: int && rref = int ( 5 ), value = 10 ; // CONFUSO: // rref es una referencia rvalue, pero value es // un simple int. /* escritura: */ int && rref = int ( 5 ), value = 10 ;
Cuando se utilizan matrices multidimensionales y referencias (o punteros) a punteros, se encuentran declaraciones más complicadas. Aunque a veces se argumenta [¿ quién? ] que dichas declaraciones son confusas y propensas a errores y que, por lo tanto, se deben evitar o reemplazar por estructuras de nivel superior, el procedimiento descrito al principio de esta sección siempre se puede utilizar sin introducir ambigüedades ni confusión.
const
se puede declarar tanto en parámetros de función como en variables ( estáticas o automáticas, incluyendo globales o locales). La interpretación varía entre 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
– de manera realista, más largas, o parámetros generales de tiempo de compilación. Una const
variable automática (variable local no estática) significa que se está produciendo una única asignación , aunque se puede usar un valor diferente cada vez, como 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 propio puntero, en paso por referencia) no añade 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 única asignación). Por esta razón, algunos prefieren usar const
en parámetros solo para paso por referencia, donde cambia el contrato, pero no para paso por valor, donde expone la implementación.
Para aprovechar el enfoque de diseño por contrato para los tipos definidos por el usuario (estructuras y clases), que pueden tener métodos además de datos de miembros, el programador puede etiquetar los métodos de instancia como const
si no modificaran los miembros de datos del objeto. const
Por lo tanto, aplicar el calificador a los 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 de Microsoft o Extensiones administradas para C++ . Si bien los métodos pueden ser llamados por objetos y no objetos por igual, los no métodos solo pueden ser invocados por objetos no objetos. El modificador en un método de instancia se aplica al objeto al que apunta 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 & nonConstC , C const & constC ) { int y = nonConstC.Get ( ) ; // Ok int x = constC.Get ( ) ; // Ok : Get () es constante nonConstC . Set ( 10 ); // Ok: nonConstC es modificable constC . Set ( 10 ); // Error! Set() es un método no constante y constC es un objeto calificado como constante }
this
En el código anterior, el puntero implícito " " Set()
tiene el tipo " C *const
"; mientras que el this
puntero " " Get()
tiene el 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 const
método que no sea un método con el mismo nombre (pero posiblemente con usos muy diferentes) en una clase para dar cabida a ambos tipos de invocadores. Considere lo siguiente:
clase MyArray { int datos [ 100 ]; público : int & Get ( int i ) { devolver datos [ i ]; } int const & Get ( 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. array . Get ( 5 ) = 42 ; // ¡OK! (Llamada: int y MyArray::Get(int)) constArray . Get ( 5 ) = 42 ; // ¡Error! (Llamada: int const y MyArray::Get(int) const) }
El const
carácter del objeto que realiza la llamada determina qué versión de MyArray::Get()
se invocará y, por lo tanto, si se le proporciona al invocador una referencia con la que puede manipular o solo observar los datos privados en el objeto. Los dos métodos tienen firmas diferentes técnicamente porque sus this
punteros " " tienen tipos diferentes, lo que permite al compilador elegir el correcto. (Devolver una const
referencia a un int
, en lugar de simplemente devolver el int
valor de by , puede ser excesivo en el segundo método, pero se puede utilizar la misma técnica para tipos arbitrarios, como en la Biblioteca de plantillas estándar ).
Existen varias lagunas en la corrección constante pura en C y C++. Existen principalmente para lograr compatibilidad con el código existente.
El primero, que se aplica únicamente a C++, es el uso de const_cast
, que permite al programador quitar el const
calificador, lo que hace que cualquier objeto sea modificable. La necesidad de quitar el calificador surge cuando se utilizan códigos y bibliotecas existentes que no se pueden modificar pero que no son constantes. Por ejemplo, considere este código:
// Prototipo de una función que no podemos cambiar pero que sabemos que no modifica el puntero pasado. void LibraryFunc ( int * ptr , int size ); void CallLibraryFunc ( int const * ptr , int size ) { LibraryFunc ( ptr , size ); // ¡Error! Se elimina el calificador const int * nonConstPtr = const_cast < int *> ( ptr ); // Quitar el calificador LibraryFunc ( nonConstPtr , size ); // OK }
Sin embargo, cualquier intento de modificar un objeto que se declara 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 al que apunta ptr
.
El lenguaje C necesita una escapatoria porque existe una determinada situación. Se permite definir variables con una duración de almacenamiento estática 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 ya sea que la variable de duración estática esté declarada 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 una conversión de tipos en el lado izquierdo de una asignación posterior, const
se puede escribir en una variable, eliminando efectivamente el const
atributo e "inicializándola" con elementos no constantes como otras const
variables y similares. Escribir en una const
variable de esta manera puede funcionar como se pretende, pero causa un comportamiento indefinido y contradice seriamente la corrección de las constantes:
size_t const bufferSize = 8 * 1024 ; size_t const userTextBufferSize ; //el valor inicial depende de const bufferSize, 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 [11] se aplica tanto a C como a C++. En concreto, los lenguajes dictan que los punteros y las referencias a miembros son "superficiales" con respecto a la const
naturaleza de sus propietarios, es decir, un objeto contenedor que const
tiene todos const
los miembros excepto los punteros (y los árbitros) de los miembros sigue siendo mutable. Para ilustrarlo, considere este código C++:
estructura S { int val ; int * ptr ; }; void Foo ( S const & s ) { int i = 42 ; s.val = i ; // Error: s es constante, por lo que val es una constante int s.ptr = & i ; // Error : s es constante , por lo que ptr es un puntero constante a int * s.ptr = i ; // OK: los datos a los que apunta ptr siempre son mutables, // aunque a veces esto no sea deseable }
Aunque el objeto s
pasado a Foo()
es constante, lo que hace que todos sus miembros sean constantes, el puntero accesible a través de él 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 el único propietario del puntero. Por esta razón, Meyers sostiene que el valor predeterminado para los punteros y referencias de miembros debería ser la "profundidad" const
, que podría ser anulada por un mutable
calificador cuando el puntero 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 requerida ] , esta laguna sigue abierta en C y C++.
Esta última laguna se puede cerrar utilizando 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 que lo contiene tampoco puede copiarse mediante la semántica habitual) o permiten otras lagunas al permitir la eliminación de const
la -idad a través de una copia inadvertida o intencional.
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 const
puntero no 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 versión "no const
".
El uso del sistema de tipos para expresar constancia conduce a varias 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++, 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, ya que algunos proyectos y organizaciones lo usan 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 debería ser del mismo tipo calificado que una entrada. En otras palabras, para estas funciones, si la entrada es constante (calificada como constante), el valor de retorno también debería serlo, pero si la entrada es variable (no const
calificada como constante), el valor de retorno también debería serlo. Debido a que la firma de tipo de estas funciones difiere, se requieren dos funciones (o potencialmente más, en 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
; esta observación es atribuida por Ritchie a Tom Plum a mediados de la década de 1980. [13] La strchr
función ubica 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 el valor de retorno suele ser utilizado por el llamador para modificar la cadena, como por ejemplo:
si ( p = strchr ( q , '/' )) * p = ' ' ;
Por lo tanto, por un lado, la cadena de entrada puede ser const
(ya que no es modificada por la función), y si la cadena de entrada es const
el valor de retorno también debería ser (simplemente porque podría devolver exactamente el puntero de entrada, si el primer carácter coincide), pero por otro lado, el valor de retorno no debería ser const
si la cadena original no era const
, ya que el llamador puede desear usar el puntero para modificar la cadena original.
En C++ esto se hace a través de una sobrecarga de funciones , generalmente implementada a través de 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 a través de la inout
palabra clave, que actúa como un comodín para constante, inmutable o no calificado (variable), dando como resultado: [14] [c]
inout ( char )* strchr ( inout ( char )* s , int c );
Sin embargo, en C ninguna de estas cosas es posible ya que C no tiene sobrecarga de funciones y, en cambio, esto se maneja con una sola función donde la entrada es constante pero la salida se puede escribir:
char * strchr ( char const * s , int c );
Esto permite el código C idiomático, pero elimina el calificador constante si la entrada en realidad estaba calificada como constante, lo que viola la seguridad de tipos. Esta solución fue propuesta por Ritchie y posteriormente adoptada. Esta diferencia es una de las fallas de compatibilidad de C y C++ .
Desde C23 , este problema se resuelve 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 se pueden modificar a través de 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 alcanzable a través de un objeto o es const
o respectivamente .immutable
const
immutable
Ejemplo de constante vs. inmutable en D
int [] foo = new int [ 5 ]; // foo es mutable. const int [] bar = foo ; // bar es una vista constante de datos mutables. immutable int [] baz = foo ; // Error: todas las vistas de datos inmutables deben ser inmutables. int inmutable [] nums = new immutable ( int )[ 5 ]; // No se puede crear ninguna referencia mutable a nums. const int [] constNums = nums ; // Funciona. immutable es implícitamente convertible a const. int [] mutableNums = nums ; // Error: No se puede crear una vista mutable de datos inmutables.
Ejemplo de constante transitiva o profunda en D
clase Foo { Foo siguiente ; int num ; } inmutable Foo foo = new immutable ( Foo ); foo . next . num = 5 ; // No se compilará. foo.next es de tipo immutable(Foo). // foo.next.num es de tipo immutable(int).
const
Fue introducido por Bjarne Stroustrup en C con Clases , 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 una alternativa con alcance y tipado a las macros, se cumplió de manera análoga para las macros de tipo función mediante la inline
palabra clave. Los punteros constantes y la * const
notación fueron sugeridos por Dennis Ritchie y, por lo tanto, adoptados. [18]
const
fue adoptado en C como parte de la estandarización, y aparece en C89 (y versiones posteriores) junto con el otro calificador de tipo, volatile
. [19] Un calificador adicional, noalias
, fue sugerido en la reunión de diciembre de 1987 del comité X3J11, pero fue rechazado; su objetivo fue finalmente cumplido por la restrict
palabra clave en C99 . Ritchie no apoyó mucho estas adiciones, argumentando que no "tenían su peso", pero en última instancia no argumentó a favor de su eliminación del estándar. [20]
Posteriormente, D heredó const
de C++, donde se lo conoce como un constructor de tipo (no un calificador de tipo ) y agregó dos constructores de tipo más, immutable
y inout
, para manejar casos de uso relacionados. [d]
Otros lenguajes no siguen a C/C++ en lo que respecta a incluir la constancia como parte del tipo, aunque a menudo tienen construcciones superficialmente similares y pueden usar la const
palabra clave. Por lo general, esto solo se usa para constantes (objetos constantes).
C# tiene una const
palabra clave, pero con una semántica radicalmente diferente y más simple: significa una constante de 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 en tiempo de compilación en lugar de formar parte del tipo. Sin embargo, en Nim, una constante se puede declarar a partir de cualquier expresión que se pueda evaluar en tiempo de compilación. [21] En C#, solo los tipos integrados de C# se pueden declarar como const
; los tipos definidos por el usuario, incluidas las clases, las estructuras y las matrices, no se pueden declarar como 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 orientado a objetos diferente para los miembros de objeto, que es el origen del nombre.
La especificación del lenguaje Java la considera const
una palabra clave reservada (es decir, una que no se puede usar como identificador de variable), pero no le asigna ninguna semántica: es una palabra reservada (no se puede usar en identificadores) pero no una palabra clave (no tiene un significado especial). La palabra clave se incluyó como un medio para que los compiladores de Java detecten y adviertan sobre el uso incorrecto de las palabras clave de C++. [23] Existe un ticket de solicitud de mejora para implementar const
la corrección en el Java Community Process , pero se cerró en 2005 sobre la base de que era imposible implementarlo de manera compatible con versiones anteriores. [24]
La Ada 83 contemporánea tenía independientemente la noción de un objeto constante y una constant
palabra clave, [25] [e] con parámetros de entrada y parámetros de bucle siendo implícitamente constantes. Aquí, la constant
es una propiedad del objeto, no del tipo.
JavaScript tiene una const
declaración que define una variable de á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 potencialmente, como si la variable hace referencia a un objeto y se modifica una propiedad de este. [26]
const
es parte del tipo derivado más externo en una declaración, los punteros 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 los 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