Quem é quem em C++: lvalue, rvalue, xvalue e outros «valores»

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O que está por trás das letras l, r, x, gl e pr? Entendendo as categorias de valores em C++

\n\n

Se você já escreveu código em C++ e recebeu um erro como "cannot bind non-const lvalue reference to an rvalue", então você já encontrou essa «mágica». As categorias de valores — lvalue, rvalue, xvalue, glvalue e prvalue — não são apenas uma classificação acadêmica. Elas são a base sobre a qual se sustenta a semântica de movimento (move semantics) e o encaminhamento perfeito (perfect forwarding).

\n\n

Vamos entender de uma vez por todas o que cada letra significa, como diferenciá-las na prática e por que sem esse conhecimento é impossível escrever C++ moderno e eficiente.

\n\n

Breve história: de onde vieram esses cinco termos?

\n\n

Originalmente, ainda nos tempos do C, existiam apenas dois conceitos: lvalue (valor à esquerda — o que pode ficar à esquerda de uma atribuição) e rvalue (valor à direita — o que fica à direita). Tudo era simples: variável é lvalue, número é rvalue.

\n\n

Com o surgimento do C++11 e da semântica de movimento, esse quadro simples deixou de funcionar. Surgiram referências a rvalue (&&) e a necessidade de distinguir objetos temporários «moribundos». Assim nasceu a nova classificação, que o padrão C++ descreve através de cinco categorias.

\n\n

Veja a hierarquia (diagrama em forma de texto):

\n\n
        expression
        /        \
   glvalue      rvalue
   /      \    /      \
lvalue   xvalue   prvalue
\n\n

Guarde esta imagem — ela é a chave para tudo.

\n\n

lvalue (left value) — objetos «persistentes»

\n\n

lvalue é uma expressão que se refere a um objeto que possui um endereço na memória. Esse objeto não desaparece após o término da expressão. Você pode obter seu endereço, modificá-lo, e ele continuará existindo.

\n\n

Exemplos de lvalue:

\n
    \n
  • Nome de variável: int x = 5; x é um lvalue.
  • \n
  • Ponteiro desreferenciado: *ptr.
  • \n
  • Referência a uma variável: int& ref = x; ref.
  • \n
  • Literal de string: "hello" (sim, é um lvalue, embora constante).
  • \n
\n\n

Código:

\n
int main() {\n    int a = 10;\n    int* p = &a;   // Obter endereço — ok, a é lvalue\n    int& ref = a;  // Referência a lvalue\n\n    // &(a + 1);   // Erro! a+1 é rvalue, sem endereço\n    return 0;\n}
\n\n

rvalue (right value) — valores «descartáveis»

\n\n

rvalue é uma expressão que não possui um endereço permanente na memória. Ela existe apenas no momento da avaliação da expressão. Você não pode obter seu endereço (com raras exceções).

\n\n

Os rvalue incluem:

\n
    \n
  • Literais (exceto strings): 42, 3.14f, true.
  • \n
  • Resultados de operações aritméticas: a + b.
  • \n
  • Objetos temporários: std::string("temp").
  • \n
  • Expressões lambda.
  • \n
\n\n

Código:

\n
int foo() { return 5; }\n\nint main() {\n    int x = 42;       // 42 — rvalue\n    int y = x + 10;   // x+10 — rvalue\n    // int* p = &(x + 10); // Erro! Não é possível obter endereço de rvalue\n    return 0;\n}
\n\n

prvalue (pure rvalue) — rvalue «puros»

\n\n

prvalue é o rvalue clássico do C++98. É um valor temporário que não tem nome e não está vinculado à memória. É o prvalue que normalmente chamamos de rvalue na linguagem coloquial.

\n\n

Exemplos de prvalue:

\n
    \n
  • Números, enumerações, resultados de operadores.
  • \n
  • Chamada de função que retorna uma não-referência: std::to_string(123).
  • \n
  • Objeto temporário criado explicitamente: MyClass().
  • \n
\n\n

xvalue (expiring value) — objetos «moribundos»

\n\n

Aqui começa a parte mais interessante. xvalue é um objeto que será destruído em breve, mas seus recursos ainda podem ser «salvos» (movidos). É uma po

Blogs

Book Recommendations