Přeskočit na obsah

Reference: Lvalue, Rvalue & Pitfalls

Prozkoumejte lvalue a rvalue reference, porozumějte hodnotovým kategoriím C++, naučte se pravidla srážení referencí a přeposílání referencí a vyhněte se visícím referenčním chybám.

Lvalue References

lvalue reference (T&) je alias — jiný název pro existující objekt. Na rozdíl od ukazatelů musí být odkazy musí inicializovat, nesmí být null a nelze je znovu usadit, aby odkazovaly na jiný objekt.

Odkazy Lvalue se vážou na lvalues — výrazy, které mají identitu (můžete si vzít jejich adresu). Proměnné, prvky pole a dereferencované ukazatele jsou všechny lvalues. Nekonstantní odkaz lvalue (int&) se nemůže vázat na dočasný (rvalue), protože úprava dočasnosti je téměř vždy chybou.

Lvalue Reference Basics

Lvalue reference jsou tahounem C++ – používají se pro pass-by-reference, přetěžování operátorů a zamezení kopírování.

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!"
}

Hodnotové kategorie v C++

C++ má přesnou taxonomii kategorií hodnot výrazu. Jejich porozumění je nezbytné pro sémantiku pohybu a dokonalé předávání:

- lvalue — má identitu, nelze ji implicitně přesunout. Příklady: proměnné,*ptr,arr[i], řetězcové literály.
- prvalue (čistá rvalue) — nemá žádnou identitu, lze ji přesunout. Příklady:42,x + y,std::string("temp"), funkce vracející hodnotu.
- xvalue (hodnota s vypršením platnosti) — má identitu A lze ji přesunout. Příklady:std::move(x),static_cast(x), člen rvalue.
- glvalue (zobecněná lvalue) — lvalue nebo xvalue (má identitu).
- rvalue — prvalue nebo xvalue (lze přesunout z).

Klíčový poznatek: referenční proměnná rvalue je sama o sobě lvalue – má jméno a adresu. To je zdrojem mnoha zmatků a právě proto existujestd::forward.

Reference Rvalue (T&&)

Reference Rvalue se vážou k rvalues ​​(dočasné objekty astd::move'd objekty). Jsou základem sémantiky pohybu – umožňují vám zjistit, kdy má být objekt zničen, a ukrást jeho zdroje.

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
}

Sbalení referencí a předání referencí

Když se odkazy spojí v šablonách, platí pravidla sbalení referencí:

-T& &se zhroutí naT&
-T& &&se zhroutí naT&
-T&& &se zhroutí naT&
-T&& &&se zhroutí naT&&

Pravidlo je jednoduché: pokud je kterýkoli odkaz odkazem lvalue, výsledkem je odkaz lvalue. Pouze&& &&dává&&.

Přesměrovací odkaz (také nazývaný univerzální odkaz) jeT&&, kdeTje odvozený parametr šablony. Může se vázat jak na lvalues, tak na rvalues.auto&&je také reference pro předávání. Alestd::vector&&NENÍ —Tje již znám.const T&&také není odkaz na přesměrování.

Úskalí

visící odkaz odkazuje na zničený objekt. Na rozdíl od visících ukazatelů zde není žádná kontrolanullptr– kód tiše použije nesmyslná data nebo se zhroutí.

Běžné příčiny:

- Vrácení odkazu na lokální proměnnou — proměnná je zničena, když se funkce vrátí.
- Zachycení odkazem v lambda, která přežije odkazovanou proměnnou.
- const auto&na dočasný uvnitř výrazu —const auto& val = get_map()["key"]visí, pokudget_map()vrátí hodnotu, protože dočasná mapa je zničena středníkem.
- Dočasná smyčka for založená na rozsahu —for (auto& x : get_vector())je v pořádku (dočasná životnost smyčky), alefor (auto& x : get_wrapper().get_vector())může viset, pokud je obal zničen.

Doživotní prodloužení s const T&

Referenceconstlvalue může prodloužit životnost dočasnosti, ale pouze když je přímo vázaná. Toto NEFUNGUJE prostřednictvím volání funkcí nebo řetězců přístupu členů.

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
}
Klíčové poznatky
  • Odkazy na Lvalue (T&) jsou aliasy – musí být inicializovány, nemohou být nulové nebo znovu usazené
  • const T&can bind to temporaries and extends their lifetime (but only when directly bound)
  • Reference Rvalue (T&&) se vážou na provizorní položky a umožňují sémantiku přesunu
  • A named rvalue reference is itself an lvalue — this is critical to understand
  • Reference collapsing: any&in the chain makes the result&
  • Forwarding references (T&&with deducedT) bind to both lvalues and rvalues

Kvíz — Otestujte své znalosti

(15 XP)

1. Jaká je vzhledem k`void f(std::string&& s)`hodnotová kategorie`s`uvnitř`f`?

2. Které pravidlo sbalení referencí je správné?

3. Kdy funguje prodloužení životnosti`const T&`pro dočasné?