INTRODUÇÃO
ISOC ++ 11 (oficialmente ISO / IEC 14882: 2011) é a versão mais recente do padrão da linguagem de programação C ++. Ele contém alguns novos recursos e conceitos, por exemplo:
- referências rvalue
- Categorias de valor da expressão xvalue, glvalue, prvalue
- mover semântica
Se quisermos entender os conceitos das novas categorias de valor de expressão, precisamos estar cientes de que existem referências rvalue e lvalue. É melhor saber que rvalues podem ser passados para referências não constantes de rvalue.
int& r_i=7; // compile error
int&& rr_i=7; // OK
Podemos obter alguma intuição dos conceitos de categorias de valor se citarmos a subseção Lvalues e rvalues do rascunho N3337 (o rascunho mais semelhante ao padrão publicado ISOC ++ 11).
3.10 Valores e valores [basic.lval]
1 As expressões são categorizadas de acordo com a taxonomia na Figura 1.
- Um lvalue (assim chamado, historicamente, porque lvalues pode aparecer no lado esquerdo de uma expressão de atribuição) designa uma função ou um objeto. [Exemplo: Se E é uma expressão do tipo ponteiro, * E é uma expressão de valor l que se refere ao objeto ou função para a qual E aponta. Como outro exemplo, o resultado da chamada de uma função cujo tipo de retorno é uma referência lvalue é um lvalue. Exemplo final]
- Um xvalue (um valor "eXpiring") também se refere a um objeto, geralmente próximo ao final de sua vida útil (para que seus recursos possam ser movidos, por exemplo). Um xvalue é o resultado de certos tipos de expressões que envolvem referências a rvalue (8.3.2). [Exemplo: o resultado da chamada de uma função cujo tipo de retorno é uma referência rvalue é um xvalue. Exemplo final]
- Um glvalue (lvalue "generalizado") é um lvalue ou um xvalue.
- Um rvalue (assim chamado, historicamente, porque rvalues pode aparecer no lado direito de uma expressão de atribuição) é um xvalue, um
objeto temporário (12.2) ou seu subobjeto ou um valor que não é
associado a um objeto.
- Um prvalor (valor "puro") é um valor que não é um valor x. [Exemplo: o resultado da chamada de uma função cujo tipo de retorno não é uma
referência é um prvalor. O valor de um literal como 12, 7.3e5 ou
true também é um pré-valor. Exemplo final]
Toda expressão pertence a exatamente uma das classificações fundamentais nessa taxonomia: lvalue, xvalue ou prvalue. Essa propriedade de uma expressão é chamada de categoria de valor.
Mas não tenho muita certeza de que essa subseção seja suficiente para entender claramente os conceitos, porque "geralmente" não é realmente geral "," no final de sua vida útil "não é realmente concreto", não é muito claro "envolver referências de valor", e "Exemplo: o resultado da chamada de uma função cujo tipo de retorno é uma referência rvalue é um xvalue". Parece que uma cobra está mordendo o rabo.
CATEGORIAS DE VALOR PRIMÁRIO
Toda expressão pertence a exatamente uma categoria de valor primário. Essas categorias de valor são lvalue, xvalue e prvalue.
lvalues
A expressão E pertence à categoria lvalue se, e somente se E, se refere a uma entidade que JÁ teve uma identidade (endereço, nome ou alias) que a torna acessível fora de E.
#include <iostream>
int i=7;
const int& f(){
return i;
}
int main()
{
std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.
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;
}
xvalues
A expressão E pertence à categoria xvalue se, e somente se, for
- o resultado da chamada de uma função, implícita ou explicitamente, cujo tipo de retorno é uma referência rvalue ao tipo de objeto que está sendo retornado, ou
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;
}
- uma conversão para uma referência rvalue ao tipo de objeto, ou
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;
}
- uma expressão de acesso de membro da classe que designa um membro de dados não estáticos do tipo não de referência no qual a expressão do objeto é um valor x, ou
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;
}
- uma expressão de ponteiro para membro em que o primeiro operando é um valor x e o segundo operando é um ponteiro para o membro de dados.
Observe que o efeito das regras acima é que referências de rvalue nomeadas a objetos são tratadas como lvalues e referências de rvalue não nomeadas a objetos são tratadas como xvalues; As referências rvalue para funções são tratadas como lvalues, nomeadas ou não.
#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
A expressão E pertence à categoria prvalue se e somente se E não pertence à categoria lvalue nem à categoria 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;
}
CATEGORIAS DE VALOR MISTO
Existem mais duas importantes categorias de valores mistos. Essas categorias de valor são categorias rvalue e glvalue.
rvalues
A expressão E pertence à categoria rvalue se e somente se E pertencer à categoria xvalue ou à categoria prvalue.
Observe que essa definição significa que a expressão E pertence à categoria rvalue se e somente se E se refere a uma entidade que não teve nenhuma identidade que a torna acessível fora de E YET.
glvalues
A expressão E pertence à categoria glvalue se e somente se E pertencer à categoria lvalue ou à categoria xvalue.
UMA REGRA PRÁTICA
Scott Meyer publicou uma regra prática muito útil para distinguir rvalues de lvalues.
- Se você pode pegar o endereço de uma expressão, a expressão é um valor l.
- Se o tipo de uma expressão é uma referência lvalue (por exemplo, T & ou const T & etc.), essa expressão é um lvalue.
- Caso contrário, a expressão é um rvalue. Conceitualmente (e geralmente também de fato), rvalues correspondem a objetos temporários, como aqueles retornados de funções ou criados por meio de conversões implícitas de tipo. A maioria dos valores literais (por exemplo, 10 e 5.3) também são rvalues.