stringtranslate.com

const (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 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 .

Introducción

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 constes 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;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 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 constes 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 fdebe 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 constmenudo 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).

Consecuencias

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, constla 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 consta menudo no se puede escribir.

Distinción con las constantes

Si bien una constante no cambia su valor mientras el programa se está ejecutando, un objeto declarado constpuede 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 consty 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).

Otros usos

Además, una función miembro (no estática) puede declararse 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 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.

Sintaxis

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 consthace 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]

Tipos de datos simples

Para los tipos de datos simples que no son punteros, la aplicación del constcalificador 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 constdos veces (por ejemplo, const char consto char const const) genera una advertencia, pero no un error.

Punteros y referencias

En el caso de los tipos de puntero y de referencia, el significado de constes 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 constpuntero a un valor escribible, un puntero escribible a un constvalor o constun puntero a constun valor.Un constpuntero 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 constlos punteros. constPor 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 constpuntero a un constobjeto 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 }       

Convención C

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 *ptres un int, mientras que la forma de referencia ptres un puntero a un int. Por lo tanto, constmodifica 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 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 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               

Convención de 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 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 * constse 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í:

  1. Encuentra el identificador cuya declaración quieres entender
  2. leer lo más lejos 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 encontró en el paso anterior)
  4. Cuando hayas llegado al principio de la declaración, habrás terminado. Si no, continúa con el paso 2, más allá del último paréntesis de cierre que coincidió.

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 constcolocar 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.

Parámetros y variables

constse 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 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.14159– de manera realista, más largas, o parámetros generales de tiempo de compilación. Una constvariable 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 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 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 consten parámetros solo para paso por referencia, donde cambia el contrato, pero no para paso por valor, donde expone la implementación.

C++

Métodos

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 constsi no modificaran los miembros de datos del objeto. constPor 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.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 & 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 }   

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

A menudo, el programador proporcionará tanto un método constcomo un constmé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 constcará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 thispunteros " " tienen tipos diferentes, lo que permite al compilador elegir el correcto. (Devolver una constreferencia a un int, en lugar de simplemente devolver el intvalor 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 ).

Lagunas en la corrección constante

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 constcalificador, 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 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 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 consto no, o ya sea que la variable de duración estática esté declarada consto no. Existe una forma no portátil de inicializar una constvariable 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, constse puede escribir en una variable, eliminando efectivamente el constatributo e "inicializándola" con elementos no constantes como otras constvariables y similares. Escribir en una constvariable 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 constnaturaleza de sus propietarios, es decir, un objeto contenedor que consttiene todos constlos 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 spasado a Foo()es constante, lo que hace que todos sus miembros sean constantes, el puntero accesible a través de él s.ptrsigue siendo modificable, aunque esto puede no ser deseable desde el punto de vista de constla corrección porque spodrí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 mutablecalificador 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 constinterfaz -correcta, pero dichas clases no admiten la semántica de copia habitual de un constobjeto (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 constla -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 constpuntero a una cadena de caracteres y devuelven un constpuntero no a 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 versión "no const".

Problemas

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 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, ya que algunos proyectos y organizaciones lo usan 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 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 constcalificada 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 strchrfunción ubica 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 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 constel 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 constsi 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 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 a través de la inoutpalabra 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. 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 se pueden modificar a través de 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 alcanzable a través de un objeto o es consto respectivamente .immutableconstimmutable

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).         

Historia

constFue 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]

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

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 inlinepalabra clave. Los punteros constantes y la * constnotación fueron sugeridos por Dennis Ritchie y, por lo tanto, adoptados. [18]

constfue 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 restrictpalabra 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ó constde C++, donde se lo conoce como un constructor de tipo (no un calificador de tipo ) y agregó dos constructores de tipo más, immutabley inout, para manejar casos de uso relacionados. [d]

Otros idiomas

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 constpalabra clave. Por lo general, esto solo se usa para constantes (objetos constantes).

C# tiene una constpalabra 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 constpalabra 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 constuna 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 constla 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 constantpalabra clave, [25] [e] con parámetros de entrada y parámetros de bucle siendo implícitamente constantes. Aquí, la constantes una propiedad del objeto, no del tipo.

JavaScript tiene una constdeclaració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]

Véase también

Notas

  1. ^ Formalmente, cuando constes parte del tipo derivado más externo en una declaración, los punteros complican la discusión.
  2. ^ Tenga en cuenta que las convenciones de sintaxis de declaración de puntero 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 utilizaría aquí una matriz en lugar de un puntero. [14]
  4. ^ D también introdujo el sharedconstructor de tipos, pero esto está relacionado con los casos de uso de volatile, no de const.
  5. ^ El estándar Ada llama a esto una " palabra reservada "; consulte ese artículo para su uso.

Referencias

  1. ^ "Elementos constantes: la referencia de Rust". doc.rust-lang.org . Consultado el 22 de junio de 2022 .
  2. ^ "El puntero this". Borrador de 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 de C++ . pág. 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 2022-12-21 . Consultado el 2022-12-21 .
  6. ^ Fahey, Mark R.; Nagle, Dan (19 de abril de 1999). "Punteros Fortran de Cray frente a punteros Fortran 90 y transferencia del Cray C90 al 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 Cray". Guía del usuario de Fortran . Oracle Corporation . 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 Cray". Guía del usuario de Fortran . Oracle Corporation . 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 de "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). Effective C++, 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 de tipo X3J11".
  14. ^ ab El lenguaje de programación D, Andrei Alexandrescu , 8.8: Propagación de un calificador desde un parámetro hasta un resultado
  15. ^ "WG14-N3020: Funciones de la biblioteca estándar que preservan calificadores" (PDF) . open-std.org . 2022-06-13. Archivado (PDF) desde el original el 2022-10-13.
  16. ^ "const(FAQ) – Lenguaje de programación D". Digitalmars.com . Consultado el 18 de agosto de 2013 .
  17. ^ Bjarne Stroustrup , "Extensiones del concepto de tipo del 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 pequeñas adiciones y ajustes, por ejemplo, los calificadores de tipo const y volátil , 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 añaden al costo de aprender y usar el lenguaje no se compensa 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 todos pueden olvidarse de él. 'Const' es al mismo tiempo más útil y más intrusivo; no se puede evitar aprender sobre él, debido a su presencia en la interfaz de la biblioteca. Sin embargo, no defiendo la extirpación de los calificadores, aunque sea solo porque es demasiado tarde".
  21. ^ Manual de Nim: Sección Const
  22. ^ const (Referencia de C#)
  23. ^ Gosling, James; Joy, Bill; Steele, Guy. "Especificación del lenguaje Java, tercera edición".
  24. ^ "Bug ID: 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 .
  25. ^ 1815A [ enlace muerto ‍ ] , 3.2.1. Declaraciones de objetos Archivado 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 del modo en de subprogramas y entradas, y los parámetros formales genéricos del modo en, 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".
  26. ^ "const". MDN . Consultado el 31 de octubre de 2017 .

Enlaces externos