Pregunta ¿Qué son los valores r, lvalues, xvalues, glvalues ​​y prvalues?


En C ++ 03, una expresión es una valor o un lvalue.

En C ++ 11, una expresión puede ser:

  1. valor
  2. lvalue
  3. xvalue
  4. glvalue
  5. prvalue

Dos categorías se han convertido en cinco categorías.

  • ¿Cuáles son estas nuevas categorías de expresiones?
  • ¿Cómo se relacionan estas nuevas categorías con las categorías rvalue y lvalue existentes?
  • ¿Las categorías rvalue y lvalue en C ++ 0x son las mismas que en C ++ 03?
  • ¿Por qué se necesitan estas nuevas categorías? Son los WG21 ¿Dios solo intenta confundirnos a los simples mortales?

1104
2017-08-30 15:02


origen


Respuestas:


Supongo que este documento podría servir como una introducción no tan breve: n3055

Toda la masacre comenzó con la semántica del movimiento. Una vez que tenemos expresiones que se pueden mover y no copiar, de repente, las reglas fáciles de comprender exigen una distinción entre las expresiones que se pueden mover y en qué dirección.

Por lo que supongo, basándome en el borrador, la distinción entre el valor de r / l permanece igual, solo en el contexto de que las cosas en movimiento se vuelven complicadas.

¿Se necesitan? Probablemente no si deseamos perder las nuevas características. Pero para permitir una mejor optimización, probablemente deberíamos abrazarlos.

Citando n3055:

  • Un lvalue (llamado, históricamente, porque lvalues ​​podría aparecer en el lado izquierdo de una tarea expresión) designa una función o un objeto. [Ejemplo: si E es un expresión del tipo de puntero, luego *E es una expresión lvalue que hace referencia a el objeto o función a la cual E puntos. Como otro ejemplo, el resultado de llamar a una función cuya el tipo de devolución es una referencia de lvalue es un lvalue.] 
  • Un xvalue (un Valor "eXpiring") también se refiere a objeto, generalmente cerca del final de su toda la vida (para que sus recursos puedan ser movido, por ejemplo). Un valor x es el resultado de ciertos tipos de expresiones que implican rvalue referencias. [Ejemplo: el resultado de llamar a una función cuya el tipo de devolución es una referencia de valor razonable es un xvalor.]
  • UN glvalue   (Lvalue "generalizado") es lvalue o un xvalue.
  • Un valor (así llamado, históricamente, porque los valores podrían aparecer en el lado derecho de un expresión de asignación) es un valor x, un objeto temporal o subobjeto de la misma, o un valor que es no asociado con un objeto.
  • UN prvalue (Valor r "puro") es un valor eso no es un xvalor [Ejemplo: el resultado de llamar a una función cuya tipo de devolución no es una referencia es una prvalue]

El documento en cuestión es una gran referencia para esta pregunta, ya que muestra los cambios exactos en el estándar que han sucedido como resultado de la introducción de la nueva nomenclatura.


530
2017-08-30 15:09



¿Cuáles son estas nuevas categorías de expresiones?

los FCD (n3092) tiene una excelente descripción:

- Un lvalue (llamado, históricamente, porque lvalues ​​podría aparecer en el   lado izquierdo de una tarea   expresión) designa una función o   un objeto. [Ejemplo: si E es un   expresión del tipo de puntero, luego   * E es una expresión lvalue que se refiere al objeto o función a la que E   puntos. Como otro ejemplo, el resultado   de llamar a una función cuyo retorno   tipo es una referencia lvalue es una   lvalue -Final ejemplo]

- Un xvalor (un   Valor "eXpiring") también se refiere a   objeto, generalmente cerca del final de su   toda la vida (para que sus recursos puedan ser   movido, por ejemplo). Un xvalor es el   resultado de ciertos tipos de expresiones   involucrando referencias de valor real (8.3.2). [   Ejemplo: el resultado de llamar a un   función cuyo tipo de devolución es un   La referencia de valor real es un valor x. -fin   ejemplo]

- Un glvalue ("generalizado"   lvalue) es un lvalue o un xvalue.

-   Un valor r (llamado, históricamente,   porque los valores pueden aparecer en   lado derecho de una tarea   expresiones) es un valor x, un temporal   objeto (12.2) o subobjeto, o   un valor que no está asociado con un   objeto.

- Un prvalue (valor r "puro") es   un valor r que no es un valor x. [   Ejemplo: el resultado de llamar a un   función cuyo tipo de devolución no es un   la referencia es un valor prve El valor de un   literal como 12, 7.3e5 o verdadero es   también un prvalue -Final ejemplo]

Cada   expresión pertenece exactamente a uno de   las clasificaciones fundamentales en   esta taxonomía: lvalue, xvalue, o   prvalue Esta propiedad de un   expresión se llama su valor   categoría. [Nota: la discusión de   cada operador incorporado en la Cláusula 5   indica la categoría del valor que   rendimientos y las categorías de valor de la   operandos que espera. Por ejemplo, el   los operadores de asignación integrados esperan   que el operando de la izquierda es un lvalue y   que el operando correcto es un prvalue   y produce un lvalue como resultado.   Los operadores definidos por el usuario son funciones,   y las categorías de valores que   esperar y el rendimiento están determinados por   sus parámetros y tipos de retorno. -fin   Nota

Te sugiero que leas toda la sección 3.10 Lvalues ​​y valores aunque.

¿Cómo se relacionan estas nuevas categorías con las categorías rvalue y lvalue existentes?

De nuevo:

Taxonomy

¿Las categorías rvalue y lvalue en C ++ 0x son las mismas que en C ++ 03?

La semántica de los valores r ha evolucionado particularmente con la introducción de la semántica de movimientos.

¿Por qué se necesitan estas nuevas categorías?

Entonces esa construcción / asignación de movimiento podría ser definida y apoyada.


303
2017-08-31 08:08



Comenzaré con tu última pregunta:

¿Por qué se necesitan estas nuevas categorías?

El estándar C ++ contiene muchas reglas que tratan con la categoría de valor de una expresión. Algunas reglas hacen una distinción entre lvalue y rvalue. Por ejemplo, cuando se trata de resolución de sobrecarga. Otras reglas hacen una distinción entre glvalue y prvalue. Por ejemplo, puede tener un glvalue con un tipo incompleto o abstracto pero no hay un prvalue con un tipo incompleto o abstracto. Antes de que tuviéramos esta terminología, las reglas que realmente necesitan distinguir entre glvalue / prvalue se referían a lvalue / rvalue y eran erróneas o involuntariamente contenían muchas explicaciones y excepciones a la regla a la "... a menos que el valor r se deba a un nombre desconocido referencia de valor real ... ". Por lo tanto, parece una buena idea simplemente darles a los conceptos de glvalues ​​y prvalues ​​su propio nombre.

¿Cuáles son estas nuevas categorías de expresiones?   ¿Cómo se relacionan estas nuevas categorías con las categorías rvalue y lvalue existentes?

Todavía tenemos los términos lvalue y rvalue que son compatibles con C ++ 98. Simplemente dividimos los valores en dos subgrupos, valores x y primos, y nos referimos a lvalues ​​y xvalues ​​como glvalues. Los valores X son un nuevo tipo de categoría de valor para las referencias de valor r sin nombre. Cada expresión es una de estas tres: lvalue, xvalue, prvalue. Un diagrama de Venn se vería así:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

Ejemplos con funciones:

int   prvalue();
int&  lvalue();
int&& xvalue();

Pero también no olvide que las referencias rvalue nombradas son lvalues:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}

156
2018-03-04 06:32



¿Por qué se necesitan estas nuevas categorías? ¿Los dioses del WG21 están tratando de confundirnos con simples mortales?

No creo que las otras respuestas (aunque muchas de ellas son buenas) realmente capturen la respuesta a esta pregunta en particular. Sí, estas categorías y esas existen para permitir la semántica del movimiento, pero la complejidad existe por una razón. Esta es la única regla inviolable de mover cosas en C ++ 11:

Te moverás solo cuando sea incuestionablemente seguro hacerlo.

Es por eso que existen estas categorías: poder hablar de valores en los que es seguro alejarse de ellos, y hablar de valores donde no lo es.

En la versión más antigua de las referencias de valor r, el movimiento sucedía fácilmente. También fácilmente. Fácilmente, había mucho potencial para mover cosas implícitamente cuando el usuario realmente no tenía la intención de hacerlo.

Estas son las circunstancias bajo las cuales es seguro mover algo:

  1. Cuando es un subobjeto temporal o de eso. (prvalue)
  2. Cuando el usuario tiene explícitamente dicho para moverlo.

Si haces esto:

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

¿Qué hace esto? En versiones anteriores de la especificación, antes de que entraran los 5 valores, esto provocaría un movimiento. Claro que lo hace. Pasaste una referencia rvalue al constructor y, por lo tanto, se une al constructor que toma una referencia rvalue. Eso es obvio.

Solo hay un problema con esto; no lo hiciste pedir moverlo. Oh, podrías decir que el && debería haber sido una pista, pero eso no cambia el hecho de que rompió la regla. val no es temporal porque los temporales no tienen nombres. Puede haber extendido la vida útil del temporal, pero eso significa que no es temporal; es como cualquier otra variable de pila.

Si no es temporal, y no solicitó moverlo, entonces el movimiento es incorrecto.

La solución obvia es hacer val un lvalue Esto significa que no puedes moverte de allí. Está bien; se llama así que es un lvalue.

Una vez que haces eso, ya no puedes decir eso SomeType&& significa lo mismo en todas partes. Ahora ha hecho una distinción entre referencias rvalue con nombre y referencias rvalue sin nombre. Bueno, las referencias de rvalue nombradas son lvalues; esa fue nuestra solución arriba. Entonces, ¿a qué llamamos referencias rvalue sin nombre (el valor de retorno de Func encima)?

No es un valor l, porque no puedes moverte desde un valor l. Y nosotros necesitar poder moverse devolviendo un &&; ¿De qué otra manera podrías decir explícitamente que muevas algo? Eso es lo que std::moveregresa, después de todo. No es un valor r (antiguo), porque puede estar en el lado izquierdo de una ecuación (las cosas son un poco más complicadas, ver esta pregunta y los comentarios a continuación). No es ni un valor ni un valor; es un nuevo tipo de cosas.

Lo que tenemos es un valor que puedes tratar como un valor l, excepto que es implícitamente movible desde. Lo llamamos xvalor.

Tenga en cuenta que xvalues ​​es lo que nos hace ganar las otras dos categorías de valores:

  • Un prvalue es realmente el nuevo nombre para el tipo anterior de rvalue, es decir, son los valores que no son xvalues.

  • Los glvalues ​​son la unión de xvalues ​​y lvalues ​​en un grupo, porque comparten muchas propiedades en común.

Realmente, todo se reduce a los valores x y la necesidad de restringir el movimiento a exactamente y solo a ciertos lugares. Esos lugares están definidos por la categoría rvalue; prvalues ​​son los movimientos implícitos, y xvalues ​​son los movimientos explícitos (std::move devuelve un xvalor).


130
2017-07-03 12:30



En mi humilde opinión, la mejor explicación sobre su significado nos dio Stroustrup + tener en cuenta ejemplos de Dániel Sándor y Mohan:

Stroustrup:

Ahora estaba seriamente preocupado. Claramente, nos dirigíamos a un callejón sin salida o   un desastre o ambos. Pasé el almuerzo haciendo un análisis para ver cuál   de las propiedades (de valores) eran independientes. Solo había dos   propiedades independientes:

  • has identity - es decir, dirección, un puntero, el usuario puede determinar si dos copias son idénticas, etc.
  • can be moved from - es decir, se nos permite salir a la fuente de una "copia" en algún estado indeterminado, pero válido

Esto me llevó a la conclusión de que hay exactamente tres tipos de   valores (utilizando el truco de notación de expresiones regulares de usar una letra mayúscula para   indicar un negativo - Tenía prisa):

  • iM: tiene identidad y no se puede mover desde
  • im: tiene identidad y se puede mover desde (por ejemplo, el resultado de convertir un lvalue en una referencia de valor)
  • Im: no tiene identidad y se puede mover desde la cuarta posibilidad (IM: no tiene identidad y no se puede mover) no es   útil en C++ (o, creo) en cualquier otro idioma.

Además de estas tres clasificaciones de valores fundamentales,   tener dos generalizaciones obvias que corresponden a los dos   propiedades independientes:

  • i: tiene identidad
  • m: se puede mover desde

Esto me llevó a poner este diagrama en el tablero:    enter image description here

Nombrando

Observé que teníamos una libertad limitada para nombrar: los dos puntos a   el izquierdo (etiquetado iM y i) son las personas con más o menos   formalidad han llamado lvalues y los dos puntos a la derecha   (etiquetado m y Im) son las personas con más o menos formalidad   haber llamado rvalues. Esto debe reflejarse en nuestro nombramiento. Es decir,   la "pierna" izquierda del W debe tener nombres relacionados con lvalue y el   derecha "pierna" de la W debe tener nombres relacionados con rvalue. Observo   que todo este debate / problema surge de la introducción de   rvalue referencias y semántica de movimiento. Estas nociones simplemente no existen   en el mundo de Strachey que consiste en solo rvalues y lvalues. Alguien   observó que las ideas que

  • Cada value es un lvalue o un rvalue
  • Un lvalue no es un rvalue y un rvalue no es un lvalue 

están profundamente arraigados en nuestra conciencia, propiedades muy útiles, y   Se pueden encontrar rastros de esta dicotomía en todo el borrador del estándar. Nosotros   todos estuvieron de acuerdo en que debemos preservar esas propiedades (y hacerlas   preciso). Esto limitó aún más nuestras opciones de nombres. Observé que   la redacción de la biblioteca estándar utiliza rvalue significar m (el   generalización), de modo que se preserve la expectativa y el texto del   biblioteca estándar el punto inferior derecho de la W debe ser nombrado    rvalue.

Esto condujo a una discusión enfocada de nombres. Primero, necesitábamos decidir   en lvalue. Debería lvalue media iM o la generalización i? LED   por Doug Gregor, enumeramos los lugares en la redacción del lenguaje central   donde la palabra lvalue fue calificado para significar el uno o el otro. UN   lista fue hecha y en la mayoría de los casos y en el texto más complicado / frágil    lvalue actualmente significa iM. Este es el significado clásico de lvalue   porque "en los viejos tiempos" no se movía nada; move es una noción novedosa   en C++0x. Además, nombrar el punto topleft de la W  lvalue Nos da   la propiedad de que cada valor es un lvalue o un rvalue, pero no ambos.

Entonces, el punto superior izquierdo de W es lvalue y el punto inferior derecho   es rvalue. ¿Qué significa eso los puntos inferior izquierdo y superior derecho?   El punto inferior izquierdo es una generalización del lvalue clásico,   permitiendo movimiento. Entonces es un generalized lvalue. Lo llamamos    glvalue. Puedes objetar sobre la abreviatura, pero (creo) no   con la lógica. Supusimos que en serio uso generalized lvalue   de alguna manera sería abreviado de todos modos, así que será mejor que lo hagamos   inmediatamente (o arriesgarse a la confusión). El punto superior derecho de la W es menos   general que el inferior derecho (ahora, como siempre, llamado rvalue) Ese   punto representa la noción pura original de un objeto que puede mover   desde porque no puede ser referido de nuevo (excepto por un destructor).   Me gustó la frase specialized rvalue en contraste con generalized lvalue pero pure rvalue abreviado a prvalue ganado (y   probablemente con razón) Entonces, la pierna izquierda del W es lvalue y    glvalue y la pierna derecha es prvalue y rvalue. De paso,   cada valor es un glvalue o un prvalue, pero no ambos.

Esto deja la mitad superior de la W: im; es decir, valores que tienen   identidad y se puede mover. Realmente no tenemos nada que guíe   nosotros a un buen nombre para esas bestias esotéricas. Ellos son importantes para   personas que trabajan con el texto estándar (borrador), pero es poco probable que   convertirse en un nombre familiar. No encontramos ninguna restricción real en el   nombrando para guiarnos, así que elegimos 'x' para el centro, lo desconocido,   extraño, solo xpert o incluso x-rated.

Steve showing off the final product


92
2018-01-20 13:46



INTRODUCCIÓN

ISOC ++ 11 (oficialmente ISO / IEC 14882: 2011) es la versión más reciente del estándar del lenguaje de programación C ++. Contiene algunas características y conceptos nuevos, por ejemplo:

  • referencias rvalue
  • xvalue, glvalue, prvalue categorías de valores de expresión
  • mover la semántica

Si quisiéramos comprender los conceptos de las nuevas categorías de valores de expresión, debemos tener en cuenta que existen referencias rvalue y lvalue. Es mejor saber que los valores de r se pueden pasar a referencias de valores no const.

int& r_i=7; // compile error
int&& rr_i=7; // OK

Podemos obtener cierta intuición de los conceptos de categorías de valores si citamos la subsección titulada Lvalues ​​y rvalues ​​del borrador N3337 (el borrador más similar al estándar ISOC ++ 11 publicado).

3.10 Lvalues ​​y valores [basic.lval]

1 Las expresiones se clasifican según la taxonomía en la Figura 1.

  • Un lvalue (llamado, históricamente, porque lvalues ​​podría aparecer en el lado izquierdo de una expresión de asignación) designa una función   o un objeto. [Ejemplo: si E es una expresión del tipo de puntero, entonces   * E es una expresión lvalue que se refiere al objeto o función a la que E apunta. Como otro ejemplo, el resultado de llamar a una función   cuyo tipo de devolución es una referencia lvalue es un valor l. -Final ejemplo]
  • Un xvalor (un valor "eXpiring") también se refiere a un objeto, generalmente cerca del final de su ciclo de vida (para que sus recursos puedan moverse, para   ejemplo). Un xvalor es el resultado de ciertos tipos de expresiones   involucrando referencias de valor real (8.3.2). [Ejemplo: el resultado de una llamada   una función cuyo tipo de devolución es una referencia rvalue es un xvalor. -fin   ejemplo]
  • Un glvalue (lvalue "generalizado") es un lvalue o un xvalue.
  • Un valor r (así llamado, históricamente, porque los valores r podrían aparecer en el lado derecho de una expresión de asignación) es un valor x, un
      objeto temporal (12.2) o subobjeto, o un valor que no es
      asociado con un objeto.
  • Un prvalue (valor r "puro") es un valor r que no es un valor x. [Ejemplo: el resultado de llamar a una función cuyo tipo de devolución no es un
      la referencia es un valor prve El valor de un literal como 12, 7.3e5 o
      true también es un prvalue -Final ejemplo]

Cada expresión pertenece exactamente a uno de los   clasificaciones en esta taxonomía: lvalue, xvalue o prvalue. Esta   La propiedad de una expresión se llama su categoría de valor.

Pero no estoy muy seguro de que esta subsección sea suficiente para comprender los conceptos con claridad, porque "normalmente" no es realmente general, "cerca del final de su vida útil" no es realmente concreto, "lo que implica referencias de valores" no es muy claro, y "Ejemplo: El resultado de llamar a una función cuyo tipo de devolución es una referencia rvalue es un xvalor". suena como una serpiente mordiendo su cola.

CATEGORÍAS DE VALOR PRIMARIO

Cada expresión pertenece exactamente a una categoría de valor principal. Estas categorías de valor son categorías lvalue, xvalue y prvalue.

lvalues

La expresión E pertenece a la categoría lvalue si y solo si E se refiere a una entidad que YA ha tenido una identidad (dirección, nombre o alias) que la hace accesible fuera de E.

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // This address ...
    std::cout<<&"www"<<std::endl; // ... and this address are the same.
    "www"; // The expression "www" in this row is an lvalue expression, because it refers to the same entity ...
    "www"; // ... as the entity the expression "www" in this row refers to.

    i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression i in this row refers to.

    int* p_i=new int(7);
    *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
    *p_i; // ... as the entity the expression *p_i in this row refers to.

    const int& r_I=7;
    r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
    r_I; // ... as the entity the expression r_I in this row refers to.

    f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression f() in this row refers to.

    return 0;
}

x valores

La expresión E pertenece a la categoría xvalue si y solo si es

- el resultado de llamar a una función, ya sea implícita o explícitamente, cuyo tipo de devolución es una referencia rvalue al tipo de objeto que se devuelve, o

int&& f(){
    return 3;
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.

    return 0;
}

- un lanzamiento a una referencia rvalue al tipo de objeto, o

int main()
{
    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).

    return 0;
}

- una expresión de acceso de miembro de clase que designa un miembro de datos no estáticos de tipo no de referencia en el que la expresión de objeto es un valor x, o

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.

    return 0;
}

- una expresión de puntero a miembro en la que el primer operando es un valor xy el segundo operando es un puntero al miembro de datos.

Tenga en cuenta que el efecto de las reglas anteriores es que las referencias de rvalue con nombre a los objetos se tratan como lvalues ​​y las referencias de valor r sin nombre a los objetos se tratan como xvalues; Las referencias a las funciones de rvalue se tratan como lvalues, ya sean nombrados o no.

#include <functional>

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
    As&& rr_a=As();
    rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
    std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.

    return 0;
}

prvalues

La expresión E pertenece a la categoría prvalue si y solo si E no pertenece ni a la categoría lvalue ni a la categoría xvalue.

struct As
{
    void f(){
        this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
    }
};

As f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.

    return 0;
}

CATEGORÍAS DE VALOR MIXTO

Hay otras dos categorías importantes de valores mixtos. Estas categorías de valores son categorías rvalue y glvalue.

valores

La expresión E pertenece a la categoría rvalue si y solo si E pertenece a la categoría xvalue, o a la categoría prvalue.

Tenga en cuenta que esta definición significa que la expresión E pertenece a la categoría rvalue si y solo si E se refiere a una entidad que no ha tenido ninguna identidad que la haga accesible fuera de E TODAVÍA.

glvalues

La expresión E pertenece a la categoría glvalue si y solo si E pertenece a la categoría lvalue, o a la categoría xvalue.

UNA REGLA PRÁCTICA

Scott Meyer tiene publicado una regla de oro muy útil para distinguir valores de valores de valores.

  • Si puede tomar la dirección de una expresión, la expresión es un valor l.
  • Si el tipo de una expresión es una referencia lvalue (por ejemplo, T y o const T &, etc.), esa expresión es un valor l.
  • De lo contrario, la expresión es un valor r. Conceptualmente (y típicamente también de hecho), los valores r corresponden a objetos temporales, tales   como aquellos devueltos por funciones o creados a través de un tipo implícito   conversiones La mayoría de los valores literales (por ejemplo, 10 y 5.3) también son valores.

34
2017-08-30 16:46



Las categorías de C ++ 03 están demasiado restringidas para capturar la introducción de las referencias de rvalue correctamente en los atributos de expresión.

Con la introducción de ellos, se dijo que una referencia rvalue sin nombre se evalúa como un valor r, de modo que la resolución de sobrecarga preferiría los enlaces de referencia rvalue, lo que lo haría seleccionar constructores de movimiento sobre constructores de copia. Pero se encontró que esto causa problemas por todos lados, por ejemplo con Tipos dinámicos y con calificaciones

Para mostrar esto, considere

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

En los borradores pre-xvalue, esto fue permitido, porque en C ++ 03, los valores r de los tipos no de clase nunca son calificados por cv. Pero se pretende que const se aplica en el caso de referencia rvalue, porque aquí hacer se refieren a objetos (= ¡memoria!), y la caída de const desde valores de clase no es principalmente debido a que no hay ningún objeto alrededor.

El problema para los tipos dinámicos es de naturaleza similar. En C ++ 03, los valores r de clase tienen un tipo dinámico conocido: es el tipo estático de esa expresión. Porque para tenerlo de otra manera, necesita referencias o desreferencias, que se evalúan a un valor l. Eso no es cierto con las referencias de valor r sin nombre, sin embargo, pueden mostrar un comportamiento polimórfico. Entonces para resolverlo,

  • referencias de valor r sin nombre se convierten x valores. Se pueden calificar y potencialmente tienen su tipo dinámico diferente. Lo hacen, como se esperaba, prefieren las referencias de valor real durante la sobrecarga, y no se vincularán a las referencias de valor l no const.

  • Lo que antes era un valor r (literales, objetos creados por moldes a tipos que no son de referencia) ahora se convierte en un prvalue. Tienen la misma preferencia que xvalues ​​durante la sobrecarga.

  • Lo que anteriormente era un lvalue se mantiene como un valor.

Y se realizan dos agrupaciones para capturar aquellas que pueden calificarse y pueden tener diferentes tipos dinámicos (glvalues) y aquellos donde la sobrecarga prefiere el enlace de referencia rvalue (valores)


32
2018-06-17 02:20



He luchado con esto durante mucho tiempo, hasta que encontré la explicación de cppreference.com categorías de valor.

En realidad es bastante simple, pero creo que a menudo se explica de una manera que es difícil de memorizar. Aquí se explica muy esquemáticamente. Citaré algunas partes de la página:

Categorías principales

Las categorías de valores principales corresponden a dos propiedades de expresiones:

  • tiene identidad: es posible determinar si la expresión se refiere a la misma entidad como otra expresión, como al comparar direcciones de los objetos o las funciones que identifican (obtenidas directa o indirectamente);

  • se puede mover desde: move constructor, move assignment operator u otra función sobrecarga que implemente la semántica de movimiento puede vincularse a la expresión.

Expresiones que:

  • tener identidad y no se puede mover se llaman expresiones lvalue;
  • tener identidad y se puede mover se llaman expresiones xvalue;
  • no tienen identidad y se puede mover desde se llaman expresiones prvalue;
  • no tienen identidad y no se pueden mover desde no se utilizan.

lvalue

Una expresión lvalue ("valor de la izquierda") es una expresión que tiene identidad y no se puede mover desde.

rvalue (hasta C ++ 11), prvalue (desde C ++ 11)

Una expresión prvalue ("rvalue puro") es una expresión que no tiene identidad y se puede mover desde.

xvalue

Una expresión xvalue ("expirar valor") es una expresión que tiene identidad y se puede mover desde.

glvalue

Una expresión glvalue ("lvalue generalizado") es una expresión que es un valor l o un valor x. Eso tiene identidad. Puede o no ser movido de.

rvalue (desde C ++ 11)

Una expresión rvalue ("valor correcto") es una expresión que es un valor prvalue o un valor x. Eso se puede mover desde. Puede o no tener identidad.


20
2017-08-30 15:45



¿Cómo se relacionan estas nuevas categorías con las categorías rvalue y lvalue existentes?

Un valor l de C ++ 03 sigue siendo un valor l de C ++ 11, mientras que un valor r de C ++ 03 se denomina un valor prono en C ++ 11.


15
2017-07-25 04:26