Sari la conținut

References: Lvalue, Rvalue & Pitfalls

Explorați referințele lvalue și rvalue, înțelegeți categoriile de valori C++, aflați regulile de restrângere a referințelor și referințele de redirecționare și evitați erorile de referință suspendate.

Lvalue References

An lvalue reference (T&) is an alias — another name for an existing object. Unlike pointers, references must be initialized, cannot be null, and cannot be re-seated to refer to a different object.

Lvalue references bind to lvalues — expressions that have identity (you can take their address). Variables, array elements, and dereferenced pointers are all lvalues. A non-const lvalue reference (int&) cannot bind to a temporary (rvalue) because modifying a temporary is almost always a bug.

Bazele referințelor Lvalue

Lvalue references are the workhorse of C++ — used for pass-by-reference, operator overloading, and avoiding copies.

lvalue_refs.cpp
#include <iostream>
#include <string>

void append_greeting(std::string& s) {
    s += ", welcome!";  // modifies the caller's string
}

int main() {
    int x = 10;
    int& ref = x;      // ref is an alias for x
    ref = 20;
    std::cout << x << '\n';  // 20 — x was modified through ref

    // const reference can bind to a temporary (rvalue)
    const std::string& greeting = std::string("Hello");
    // The temporary's lifetime is extended to match greeting's scope
    std::cout << greeting << '\n';  // "Hello"

    // Non-const lvalue reference CANNOT bind to rvalue:
    // std::string& bad = std::string("oops");  // COMPILER ERROR

    std::string name = "Alice";
    append_greeting(name);
    std::cout << name << '\n';  // "Alice, welcome!"
}

Value Categories in C++

C++ are o taxonomie precisă a categoriilor de valori de expresie. Înțelegerea lor este esențială pentru semantica mișcării și transmiterea perfectă:

- lvalue — are identitate, nu poate fi mutat din implicit. Exemple: variabile,*ptr,arr[i], literale șir.
- prvalue (pură rvalue) — nu are identitate, poate fi mutat din. Exemple:42,x + y,std::string("temp"), funcție care revine după valoare.
- xvalue (valoare care expiră) — are identitate ȘI poate fi mutat din. Exemple:std::move(x),static_cast(x), membru al unei valori r.
- glvalue (lvalue generalizată) — lvalue sau xvalue (are identitate).
- rvalue — prvalue sau xvalue (poate fi mutat din).

Perspectiva cheie: o variabilă de referință rvalue este ea însăși o lvalue - are un nume și o adresă. Aceasta este sursa multor confuzii și tocmai de aceea existăstd::forward.

Referințe Rvalue (T&&)

Referințele Rvalue se leagă de rvalues ​​(temporare și obiectestd::move). Ele sunt fundamentul semanticii mișcării - vă permit să detectați când un obiect este pe cale să fie distrus și să îi furați resursele.

rvalue_refs.cpp
#include <iostream>
#include <string>
#include <utility>  // std::move

void process(const std::string& s) {
    std::cout << "lvalue: " << s << '\n';
}

void process(std::string&& s) {
    std::cout << "rvalue: " << s << '\n';
    // s is an lvalue inside this function (it has a name!)
    // To move from s, you'd need std::move(s)
}

int main() {
    std::string a = "hello";
    process(a);               // calls lvalue overload
    process(std::string("world"));  // calls rvalue overload
    process(std::move(a));    // calls rvalue overload — a is now moved-from

    // IMPORTANT: a is in a valid but unspecified state
    // Only safe operations: assign to it, destroy it
    std::cout << "a after move: '" << a << "'\n";  // likely empty
}

Restrângerea și redirecționarea referințelor referințe

Când referințele se combină în șabloane, se aplică regulile de reducere a referințelor:

-T& &se prăbușește laT&
-T& &&se prăbușește laT&
-T&& &se prăbușește laT&
-T&& &&se prăbușește laT&&

Regula este simplă: dacă oricare referință este o referință lvalue, rezultatul este o referință lvalue. Numai&& &&oferă&&.

O referință de redirecționare (numită și referință universală) esteT&&, undeTeste un parametru de șablon dedus. Se poate lega atât la valorile l cât și la valorile r.auto&&este, de asemenea, o referință de redirecționare. Darstd::vector&&NU este -Teste deja cunoscut. De asemenea,const T&&nu este o referință de redirecționare.

Capcană

O referință suspendată se referă la un obiect distrus. Spre deosebire de pointerii suspendați, nu există nicio verificarenullptr- codul va folosi în tăcere date de gunoi sau se va prăbuși.

Cauze comune:

- Returnarea unei referințe la o variabilă locală — variabila este distrusă când funcția revine.
- Capturarea prin referință într-o lambda care supraviețuiește variabilei referite.
- const auto&la un temporar în interiorul unei expresii —const auto& val = get_map()["key"]este atârnând dacăget_map()revine după valoare, deoarece harta temporară este distrusă la punctul și virgulă.
- Bazat pe interval pentru buclă peste o buclă temporară —for (auto& x : get_vector())este în regulă (vie temporare pentru buclă), darfor (auto& x : get_wrapper().get_vector())poate atârna dacă învelișul este distrus.

Prelungire pe viață cu const T&

O referință lvalueconstpoate prelungi durata de viață a unui temporar, dar numai atunci când este legată direct. Acest lucru NU funcționează prin apeluri de funcție sau lanțuri de acces la membri.

lifetime_extension.cpp
#include <iostream>
#include <string>

std::string make_greeting() { return "Hello, World!"; }

const std::string& identity(const std::string& s) { return s; }

int main() {
    // SAFE: const ref directly bound to temporary
    const std::string& good = make_greeting();
    std::cout << good << '\n';  // OK — lifetime extended

    // DANGEROUS: lifetime extension does NOT propagate through functions
    const std::string& bad = identity(make_greeting());
    // bad is DANGLING — the temporary was destroyed after identity() returned
    // std::cout << bad << '\n';  // UNDEFINED BEHAVIOR

    // SAFE: just use a value
    std::string safe = make_greeting();  // move or copy-elision
    std::cout << safe << '\n';  // always safe
}
Idei cheie
  • Referințele Lvalue (T&) sunt aliasuri — trebuie să fie inițializate, nu pot fi nule sau reașezate
  • const T&se poate lega de temporare și prelungește durata de viață a acestora (dar numai atunci când este legat direct)
  • Referințele Rvalue (T&&) se leagă de temporare și permit semantica de mutare
  • O referință numită rvalue este ea însăși o lvalue - acest lucru este esențial de înțeles
  • Referința se prăbușește: orice&din lanț face rezultatul&
  • Referințele de redirecționare (T&&cuTdedus) se leagă atât la valorile l cât și la valorile r

Test — Verifică-ți cunoștințele

(15 XP)

1. Având în vedere`void f(std::string&& s)`, care este categoria de valoare a`s`în interiorul`f`?

2. Which reference collapsing rule is correct?

3. Când funcționează extensia de viață`const T&`pentru temporari?