Taxonomía de la categoría de expresión en C ++

Taxonomía de la categoría de expresión en C ++
Un cálculo es cualquier tipo de cálculo que siga un algoritmo bien definido. Una expresión es una secuencia de operadores y operandos que especifica un cálculo. En otras palabras, una expresión es un identificador o un literal, o una secuencia de ambos, unida por operadores.En la programación, una expresión puede dar lugar a un valor y/o hacer que algunos sucederan. Cuando resulta en un valor, la expresión es un glValue, rValue, lvalue, xvalue o pralente. Cada una de estas categorías es un conjunto de expresiones. Cada conjunto tiene una definición y situaciones particulares donde prevalece su significado, diferenciándolo de otro conjunto. Cada conjunto se llama categoría de valor.

Nota: Un valor o literal sigue siendo una expresión, por lo que estos términos clasifican expresiones y no realmente valores.

Glvalue y Rvalue son los dos subconjuntos de la gran expresión del conjunto. Glvalue existe en dos subconjuntos adicionales: lvalue y xvalue. RValue, el otro subconjunto para la expresión, también existe en dos subconjuntos adicionales: xvalue y praleo. Entonces, XValue es un subconjunto de glValue y rValue: es decir, xvalue es la intersección de glvalue y rValue. El siguiente diagrama de taxonomía, tomado de la especificación C ++, ilustra la relación de todos los conjuntos:

PRALAJE, XVALUE y LVALUE son los valores de categoría principal. Glvalue es la unión de Lvalues ​​y XValues, mientras que los Rvalues ​​son la unión de XValues ​​y Praluues.

Necesita conocimientos básicos en C ++ para comprender este artículo; También necesitas conocimiento del alcance en c++.

Contenido del artículo

  • Lo esencial
  • lvalor
  • hacer un praleo
  • xvalor
  • Conjunto de taxonomía de la categoría de expresión
  • Conclusión

Lo esencial

Para comprender realmente la taxonomía de la categoría de expresión, debe recordar o conocer primero las siguientes características básicas: ubicación y objeto, almacenamiento y recursos, inicialización, identificador y referencia, referencias de Lvalue y Rvalue, puntero, tienda gratuita y reutilización de un recurso.

Ubicación y objeto

Considere la siguiente declaración:

int ident;

Esta es una declaración que identifica una ubicación en la memoria. Una ubicación es un conjunto particular de bytes consecutivos en la memoria. Una ubicación puede consistir en un byte, dos bytes, cuatro bytes, sesenta y cuatro bytes, etc. La ubicación para un entero para una máquina de 32 bits es de cuatro bytes. Además, la ubicación puede ser identificada por un identificador.

En la declaración anterior, la ubicación no tiene contenido. Significa que no tiene ningún valor, ya que el contenido es el valor. Entonces, un identificador identifica una ubicación (pequeño espacio continuo). Cuando la ubicación recibe un contenido en particular, el identificador identifica tanto la ubicación como el contenido; es decir, el identificador identifica tanto la ubicación como el valor.

Considere las siguientes afirmaciones:

int ident1 = 5;
int ident2 = 100;

Cada una de estas declaraciones es una declaración y una definición. El primer identificador tiene el valor (contenido) 5, y el segundo identificador tiene el valor 100. En una máquina de 32 bits, cada una de estas ubicaciones tiene cuatro bytes de largo. El primer identificador identifica tanto una ubicación como un valor. El segundo identificador también identifica ambos.

Un objeto es una región de almacenamiento nombrada en la memoria. Entonces, un objeto es una ubicación sin valor o ubicación con un valor.

Almacenamiento y recurso de objetos

La ubicación de un objeto también se llama almacenamiento o recurso del objeto.

Inicialización

Considere el siguiente segmento de código:

int ident;
ident = 8;

La primera línea declara un identificador. Esta declaración proporciona una ubicación (almacenamiento o recurso) para un objeto entero, identificándola con el nombre, identificar. La siguiente línea coloca el valor 8 (en bits) en la ubicación identificada por identificación. La colocación de este valor es la inicialización.

La siguiente declaración define un vector con contenido, 1, 2, 3, 4, 5, identificado por VTR:

std :: vector vtr 1, 2, 3, 4, 5;

Aquí, la inicialización con 1, 2, 3, 4, 5 se realiza en la misma declaración de la definición (Declaración). El operador de asignación no se usa. La siguiente declaración define una matriz con contenido 1, 2, 3, 4, 5:

int arr [] = 1, 2, 3, 4, 5;

Esta vez, se ha utilizado un operador de asignación para la inicialización.

Identificador y referencia

Considere el siguiente segmento de código:

int ident = 4;
int & ref1 = ident;
int & ref2 = ident;
cout<< ident <<"<< ref1 <<"<< ref2 << '\n';

La salida es:

4 4 4

Ident es un identificador, mientras que Ref1 y Ref2 son referencias; Hacen referencia a la misma ubicación. Una referencia es sinónimo de un identificador. Convencionalmente, Ref1 y Ref2 son diferentes nombres de un objeto, mientras que Ident es el identificador del mismo objeto. Sin embargo, Ident todavía se puede llamar el nombre del objeto, lo que significa, Ident, Ref1 y Ref2 Nombre de la misma ubicación.

La principal diferencia entre un identificador y una referencia es que, cuando se pasa como un argumento a una función, si se pasa por el identificador, se hace una copia para el identificador en la función, mientras que si se pasa por referencia, la misma ubicación se usa dentro de la función. Entonces, pasar por identificador termina con dos ubicaciones, mientras que pasar por referencia termina con la misma ubicación.

referencia de lvalue y referencia de rValue

La forma normal de crear una referencia es la siguiente:

int ident;
ident = 4;
int & ref = ident;

El almacenamiento (recurso) se encuentra e identifica primero (con un nombre como Ident), y luego se hace una referencia (con un nombre como un REF). Al pasar como argumento a una función, se hará una copia del identificador en la función, mientras que para el caso de una referencia, la ubicación original se utilizará (mencionada) en la función.

Hoy, es posible tener una referencia sin identificarla. Esto significa que es posible crear una referencia primero sin tener un identificador para la ubicación. Esto usa &&, como se muestra en la siguiente declaración:

int && ref = 4;

Aquí, no hay una identificación anterior. Para acceder al valor del objeto, simplemente use REF ya que usaría el ident.

Con la declaración &&, no hay posibilidad de transmitir un argumento a una función por identificador. La única opción es pasar por referencia. En este caso, solo se usa una ubicación dentro de la función y no en la segunda ubicación copiada como con un identificador.

Una declaración de referencia con y se llama referencia de lvalue. Una declaración de referencia con && se llama referencia rValue, que también es una referencia de praleo (ver más abajo).

Puntero

Considere el siguiente código:

int ptdint = 5;
int *ptrint;
Ptrint = &ptdInt;
cout<< *ptrInt <<'\n';

La salida es 5.

Aquí, Ptdint es un identificador como el ident. Hay dos objetos (ubicaciones) aquí en lugar de uno: el objeto puntiagudo, ptdint identificado por ptdint y el objeto puntero, ptrint identificado por ptrint. & Ptdint Devuelve la dirección del objeto puntiagudo y la pone como el valor en el objeto Ptrint Pointer. Para devolver (obtener) el valor del objeto puntiagudo, use el identificador para el objeto puntero, como en "*Ptrint".

Nota: PtDint es un identificador y no una referencia, mientras que el nombre, Ref, mencionado anteriormente, es una referencia.

La segunda y tercera línea en el código anterior se pueden reducir a una línea, lo que lleva al siguiente código:

int ptdint = 5;
int *ptrint = &ptdInt;
cout<< *ptrInt <<'\n';

Nota: Cuando se incrementa un puntero, apunta a la siguiente ubicación, que no es una adición del valor 1. Cuando un puntero está disminuido, apunta a la ubicación anterior, que no es una resta del valor 1.

Tienda gratis

Un sistema operativo asigna memoria para cada programa que se ejecuta. Una memoria que no se asigna a ningún programa se conoce como la tienda gratuita. La expresión que devuelve una ubicación para un entero de la tienda gratuita es:

Nuevo int

Esto devuelve una ubicación para un entero que no se identifica. El siguiente código ilustra cómo usar el puntero con la tienda gratuita:

int *ptrint = new int;
*ptrint = 12;
cout<< *ptrInt <<'\n';

La salida es 12.

Para destruir el objeto, use la expresión de eliminación de la siguiente manera:

Eliminar Ptrint;

El argumento a la expresión de eliminación es un puntero. El siguiente código ilustra su uso:

int *ptrint = new int;
*ptrint = 12;
Eliminar Ptrint;
cout<< *ptrInt <<'\n';

La salida es 0, y no nada como nulo o indefinido. Eliminar reemplaza el valor de la ubicación con el valor predeterminado del tipo particular de la ubicación, luego permite la ubicación para reutilizar. El valor predeterminado para una ubicación int es 0.

Reutilización de un recurso

En la taxonomía de la categoría de expresión, reutilizar un recurso es lo mismo que reutilizar una ubicación o almacenamiento para un objeto. El siguiente código ilustra cómo se puede reutilizar una ubicación de la tienda gratuita:

int *ptrint = new int;
*ptrint = 12;
cout<< *ptrInt <<'\n';
Eliminar Ptrint;
cout<< *ptrInt <<'\n';
*ptrint = 24;
cout<< *ptrInt <<'\n';

La salida es:

12
0
24

Primero se asigna un valor de 12 a la ubicación no identificada. Entonces se elimina el contenido de la ubicación (en teoría se elimina el objeto). El valor de 24 se reasigna a la misma ubicación.

El siguiente programa muestra cómo se reutiliza una referencia entera devuelta por una función:

#incluir
usando el espacio de nombres STD;
int & fn ()
int i = 5;
int & j = i;
regresar j;

int main ()
int & myInt = fn ();
cout<< myInt <<'\n';
myint = 17;
cout<< myInt <<'\n';
regresar 0;

La salida es:

5
17

Un objeto como I, declarado en un alcance local (alcance de la función), deja de existir al final del alcance local. Sin embargo, la función fn () arriba, devuelve la referencia de i. A través de esta referencia devuelta, el nombre, myInt en la función principal (), reutiliza la ubicación identificada por I para el valor 17.

lvalor

Un LValue es una expresión cuya evaluación determina la identidad de un objeto, campo de bits o función. La identidad es una identidad oficial como identidad anterior, o un nombre de referencia de Lvalue, un puntero o el nombre de una función. Considere el siguiente código que funciona:

int myint = 512;
int & myref = myInt;
int* ptr = &myInt;
int fn ()
++ptr; --ptr;
devolver myint;

Aquí, Myint es un Lvalue; MyRef es una expresión de referencia de Lvalue; *PTR es una expresión de Lvalue porque su resultado es identificable con PTR; ++ ptr o -ptr es una expresión de Lvalue porque su resultado es identificable con el nuevo estado (dirección) de PTR, y FN es un LValue (expresión).

Considere el siguiente segmento de código:

int a = 2, b = 8;
int c = a + 16 + b + 64;

En la segunda declaración, la ubicación para 'A' tiene 2 y es identificable por 'A', y también lo es un LValue. La ubicación para B tiene 8 y es identificable por B, y también lo es un LValue. La ubicación para C tendrá la suma, y ​​es identificable por C, y también lo es un LValue. En la segunda declaración, las expresiones o valores de 16 y 64 son rValues ​​(ver más abajo).

Considere el siguiente segmento de código:

char Seq [5];
seq [0] = 'l', seq [1] = 'o', seq [2] = 'V', seq [3] = 'e', ​​seq [4] = '\ 0';
cout<< seq[2] <<'\n';

La salida es 'V';

Seq es una matriz. La ubicación para 'V' o cualquier valor similar en la matriz es identificada por SEQ [i], donde yo es un índice. Entonces, la expresión, seq [i], es una expresión de Lvalue. Seq, que es el identificador para toda la matriz, también es un LValue.

hacer un praleo

Un praleo es una expresión cuya evaluación inicializa un objeto o un campo de bits o calcula el valor del operando de un operador, según lo especificado por el contexto en el que aparece.

En la declaración,

int myint = 256;

256 es un pralente (expresión de praleo) que inicializa el objeto identificado por myint. Este objeto no se hace referencia.

En la declaración,

int && ref = 4;

4 es un pralente (expresión de praleo) que inicializa el objeto a la referencia por la referencia. Este objeto no se identifica oficialmente. REF es un ejemplo de una expresión de referencia de RValue o expresión de referencia de praleo; Es un nombre, pero no un identificador oficial.

Considere el siguiente segmento de código:

int ident;
Ident = 6;
int & ref = ident;

6 es un prvalor que inicializa el objeto identificado por identificación; el objeto también se hace referencia a la referencia. Aquí, el árbitro es una referencia de Lvalue y no una referencia de pralta.

Considere el siguiente segmento de código:

int a = 2, b = 8;
int c = a + 15 + b + 63;

15 y 63 son una constante que se calcula para sí misma, produciendo un operando (en bits) para el operador de adición. Entonces, 15 o 63 es una expresión previa.

Cualquier literal, excepto el literal de cuerdas, es un praleo (yo.mi., una expresión de pralta). Entonces, un literal como 58 o 58.53, o verdadero o falso, es un praleo. Se puede usar un literal para inicializar un objeto o se calcularía a sí mismo (en alguna otra forma en bits) como el valor de un operando para un operador. En el código anterior, el literal 2 inicializa el objeto, un. También se calcula como un operando para el operador de asignación.

¿Por qué una cuerda es literal no es un praleo?? Considere el siguiente código:

char str [] = "amor no odio";
cout << str <<'\n';
cout << str[5] <<'\n';

La salida es:

Amor no odio
norte

STR identifica toda la cadena. Entonces, la expresión, str, y no lo que identifica, es un lvalue. Cada carácter de la cadena puede ser identificado por Str [i], donde yo es un índice. La expresión, Str [5], y no el carácter que identifica, es un LValue. El literal de la cuerda es un lvalue y no un pralente.

En la siguiente declaración, una matriz literalmente inicializa el objeto, arr:

Ptrint ++ o Ptrint--

Aquí, Ptrint es un puntero a una ubicación entera. Toda la expresión, y no el valor final de la ubicación a la que señala, es un praleo (expresión). Esto se debe a que la expresión, ptrint ++ o ptrint-, identifica el primer valor original de su ubicación y no el segundo valor final de la misma ubicación. A la otra mano, -Ptrint o -Ptrint es un LValue porque identifica el único valor del interés en la ubicación. Otra forma de verlo es que el valor original calcula el segundo valor final.

En la segunda declaración del siguiente código, A o B aún se puede considerar como un praleo:

int a = 2, b = 8;
int c = a + 15 + b + 63;

Entonces, A o B en la segunda declaración es un LValue porque identifica un objeto. También es un prevalecimiento, ya que se calcula en el entero de un operando para el operador de adición.

(nuevo int), y no la ubicación que establece es un pralente. En la siguiente declaración, la dirección de devolución de la ubicación se asigna a un objeto puntero:

int *ptrint = nuevo int

Aquí, *Ptrint es un lvalue, mientras que (nuevo int) es un praleo. Recuerde, un lvalue o un pralente es una expresión. (nuevo int) no identifica ningún objeto. Devolver la dirección no significa identificar el objeto con un nombre (como Ident, arriba). En *Ptrint, el nombre, Ptrint, es lo que realmente identifica el objeto, por lo que *Ptrint es un LValue. Por otro lado, (New INT) es un praleo, ya que calcula una nueva ubicación para una dirección de valor operando para el operador de asignación =.

xvalor

Hoy, Lvalue significa valor de ubicación; Presalte significa RValue "puro" (ver qué representa el rValue a continuación). Hoy, Xvalue significa "expirado" de Lvalue.

La definición de xvalue, citada a partir de la especificación C ++, es la siguiente:

"Un xvalue es un glvalú que denota un objeto o campo de bits cuyos recursos se pueden reutilizar (generalmente porque está cerca del final de su vida). [Ejemplo: ciertos tipos de expresiones que involucran referencias de rValue producen xvalues ​​X, como una llamada a una función cuyo tipo de retorno es una referencia de RValue o un fundido a un ejemplo de tipo de referencia de rValue

Lo que esto significa es que tanto el lvalue como el pralente pueden caducar. El siguiente código (copiado de arriba) muestra cómo el almacenamiento (recurso) del Lvalue, *Ptrint se reutiliza después de que se ha eliminado.

int *ptrint = new int;
*ptrint = 12;
cout<< *ptrInt <<'\n';
Eliminar Ptrint;
cout<< *ptrInt <<'\n';
*ptrint = 24;
cout<< *ptrInt <<'\n';

La salida es:

12
0
24

El siguiente programa (copiado de arriba) muestra cómo el almacenamiento de una referencia entera, que es una referencia de Lvalue devuelto por una función, se reutiliza en la función Main ():

#incluir
usando el espacio de nombres STD;
int & fn ()
int i = 5;
int & j = i;
regresar j;

int main ()
int & myInt = fn ();
cout<< myInt <<'\n';
myint = 17;
cout<< myInt <<'\n';
regresar 0;

La salida es:

5
17

Cuando un objeto como I en la función fn () sale de alcance, naturalmente se destruye. En este caso, el almacenamiento de I todavía se ha reutilizado en la función principal ().

Las dos muestras de código anteriores ilustran la reutilización del almacenamiento de Lvalues. Es posible tener una reutilización de almacenamiento de praltos (rValues) (ver más tarde).

La siguiente cotización sobre XValue es de la especificación de C ++:

“En general, el efecto de esta regla es que las referencias de RValue nombradas se tratan como Lvalues ​​y las referencias de RValue sin nombre a los objetos se tratan como XValues. Las referencias de RValue a las funciones se tratan como Lvalues, ya sea nombradas o no." (nos vemos).

Entonces, un xvalue es un LValue o un praleo cuyos recursos (almacenamiento) pueden reutilizarse. xvalues ​​es el conjunto de intersección de Lvalues ​​y Pralues.

Hay más de xvalue que lo que se ha abordado en este artículo. Sin embargo, XValue merece un artículo completo por sí solo, por lo que las especificaciones adicionales para XValue no se abordan en este artículo.

Conjunto de taxonomía de la categoría de expresión

Otra cita de la especificación C ++:

"Nota: Históricamente, los Lvalues ​​y los Rvalues ​​se llamaron porque podían aparecer en el lado izquierdo y derecho de una tarea (aunque esto ya no es generalmente cierto); Los glValues ​​son "generalizados", los presaluicios son rValues ​​"puros" y XValues ​​están "expirados" de Lvalues. A pesar de sus nombres, estos términos clasifican las expresiones, no los valores. - Nota final "

Entonces, Glvalues ​​es el conjunto de Lvalues ​​y XValues ​​y RValues ​​de XValues ​​y Pralues ​​de la Unión. xvalues ​​es el conjunto de intersección de Lvalues ​​y Pralues.

A partir de ahora, la taxonomía de la categoría de expresión se ilustra mejor con un diagrama de Venn de la siguiente manera:

Conclusión

Un LValue es una expresión cuya evaluación determina la identidad de un objeto, campo de bits o función.

Un praleo es una expresión cuya evaluación inicializa un objeto o un campo de bits o calcula el valor del operando de un operador, según lo especificado por el contexto en el que aparece.

Un xvalue es un lvalue o un pralente, con la propiedad adicional de que sus recursos (almacenamiento) pueden reutilizarse.

La especificación C ++ ilustra la taxonomía de la categoría de expresión con un diagrama de árboles, lo que indica que hay cierta jerarquía en la taxonomía. A partir de ahora, no hay jerarquía en la taxonomía, por lo que algunos autores utilizan un diagrama de Venn, ya que ilustra la taxonomía mejor que el diagrama de los árboles.