Ricerca…


Significato della categoria di valore

Alle espressioni in C ++ viene assegnata una particolare categoria di valore, in base al risultato di tali espressioni. Le categorie di valori per le espressioni possono influire sulla risoluzione di sovraccarico della funzione C ++.

Le categorie valore determinano due proprietà importanti ma separate su un'espressione. Una proprietà è se l'espressione ha identità. Un'espressione ha identità se fa riferimento a un oggetto che ha un nome di variabile. Il nome della variabile potrebbe non essere coinvolto nell'espressione, ma l'oggetto può ancora averne uno.

L'altra proprietà è se è legale spostarsi implicitamente dal valore dell'espressione. O più nello specifico, se l'espressione, se usata come parametro di funzione, si legherà ai tipi di parametri di valore r o meno.

C ++ definisce 3 categorie di valori che rappresentano la combinazione utile di queste proprietà: lvalue (espressioni con identità ma non mobili da), xvalue (espressioni con identità che sono mobili da) e prvalore (espressioni senza identità che sono mobili da). C ++ non ha espressioni che non hanno identità e non possono essere spostate da.

C ++ definisce altre due categorie di valori, ciascuna basata unicamente su una di queste proprietà: glvalue (espressioni con identità) e rvalue (espressioni che possono essere spostate da). Questi agiscono come raggruppamenti utili delle categorie precedenti.

Questo grafico serve come illustrazione:

Grafico delle categorie di valore nel linguaggio C ++

prvalue

Un'espressione prvalore (pure-rvalue) è un'espressione priva di identità, la cui valutazione viene in genere utilizzata per inizializzare un oggetto e da cui può essere implicitamente spostato. Questi includono, ma non sono limitati a:

  • Espressioni che rappresentano oggetti temporanei, come std::string("123") .
  • Una espressione di chiamata di funzione che non restituisce un riferimento
  • Un letterale ( tranne un letterale stringa - quelli sono lvalue), tale è 1 , true , 0.5f o 'a'
  • Un'espressione lambda

L'indirizzo incorporato dell'operatore ( & ) non può essere applicato su queste espressioni.

xValue

Un'espressione xvalue (valore eXpiring) è un'espressione che ha identità e rappresenta un oggetto da cui può essere implicitamente spostato. L'idea generale con le espressioni xvalue è che l'oggetto che rappresentano sarà presto distrutto (da qui la parte "eXpiring"), e quindi spostarsi implicitamente da esse va bene.

Dato:

struct X { int n; };
extern X x;

4;                   // prvalue: does not have an identity
x;                   // lvalue
x.n;                 // lvalue
std::move(x);        // xvalue
std::forward<X&>(x); // lvalue
X{4};                // prvalue: does not have an identity
X{4}.n;              // xvalue: does have an identity and denotes resources
                     // that can be reused

lvalue

Un'espressione lvalue è un'espressione che ha identità, ma non può essere spostata implicitamente da. Tra queste sono espressioni che consistono in un nome di variabile, nome di funzione, espressioni che sono usi di operatore di deferenziazione ed espressioni che si riferiscono a riferimenti di lvalue.

Il lvalue tipico è semplicemente un nome, ma i lvalue possono venire anche in altri gusti:

struct X { ... };

X x;         // x is an lvalue
X* px = &x;  // px is an lvalue
*px = X{};   // *px is also an lvalue, X{} is a prvalue

X* foo_ptr();  // foo_ptr() is a prvalue
X& foo_ref();  // foo_ref() is an lvalue

Inoltre, mentre la maggior parte dei valori letterali (ad es. 4 , 'x' , ecc.) Sono prvalues, i letterali stringa sono lvalue.

glvalue

Un'espressione glvalue (a "lvalue generalizzato") è qualsiasi espressione che abbia identità, indipendentemente dal fatto che possa essere spostata o meno. Questa categoria include lvalue (espressioni che hanno identità ma non possono essere spostate da) e xvalues ​​(espressioni che hanno identità e possono essere spostate da), ma esclude prvalues ​​(espressioni senza identità).

Se un'espressione ha un nome , è un glivalue:

struct X { int n; };
X foo();

X x;
x; // has a name, so it's a glvalue
std::move(x); // has a name (we're moving from "x"), so it's a glvalue
              // can be moved from, so it's an xvalue not an lvalue

foo(); // has no name, so is a prvalue, not a glvalue
X{};   // temporary has no name, so is a prvalue, not a glvalue
X{}.n; // HAS a name, so is a glvalue. can be moved from, so it's an xvalue

rvalue

Un'espressione rvalue è qualsiasi espressione che può essere implicitamente spostata da, indipendentemente dal fatto che abbia identità.

Più precisamente, le espressioni rvalue possono essere utilizzate come argomento di una funzione che accetta un parametro di tipo T && (dove T è il tipo di expr ). Solo le espressioni rvalue possono essere date come argomenti a tali parametri di funzione; se viene utilizzata un'espressione non rvalore, la risoluzione di sovraccarico selezionerà qualsiasi funzione che non utilizza un parametro di riferimento rvalue. E se nessuno esiste, allora si ottiene un errore.

La categoria delle espressioni rvalue include tutte le espressioni xvalue e prvalue e solo quelle espressioni.

La funzione di libreria standard std::move esiste per trasformare esplicitamente un'espressione non rvalore in un valore rvalue. Più specificamente, trasforma l'espressione in un valore x, poiché anche se prima era un'espressione di prvalore priva di identità, passandola come parametro a std::move , ottiene l'identità (il nome del parametro della funzione) e diventa un valore x.

Considera quanto segue:

std::string str("init");                       //1
std::string test1(str);                        //2
std::string test2(std::move(str));             //3

str = std::string("new value");                //4 
std::string &&str_ref = std::move(str);        //5
std::string test3(str_ref);                    //6

std::string ha un costruttore che accetta un singolo parametro di tipo std::string&& , comunemente chiamato "costruttore di mosse". Tuttavia, la categoria di valore dell'espressione str non è un valore di rvalue (in particolare è un lvalue), quindi non può chiamare quel sovraccarico del costruttore. Invece, chiama const std::string& overload, il costruttore di copie.

La linea 3 cambia le cose. Il valore di ritorno di std::move è un T&& , dove T è il tipo base del parametro passato. Quindi std::move(str) restituisce std::string&& . Una chiamata di funzione il cui valore di ritorno è un riferimento di rvalue è un'espressione di valore (in particolare un valore x), quindi può chiamare il costruttore di movimento di std::string . Dopo la riga 3, str è stato spostato da (chi è il contenuto non è ora definito).

La riga 4 passa temporaneamente all'operatore di assegnazione di std::string . Questo ha un sovraccarico che accetta uno std::string&& . L'espressione std::string("new value") è un'espressione rvalue (in particolare un valore nominale), quindi può chiamare tale overload. Pertanto, il temporaneo viene spostato in str , sostituendo i contenuti indefiniti con contenuti specifici.

La riga 5 crea un riferimento str_ref denominato str_ref che fa riferimento a str . È qui che le categorie di valore si confondono.

Vedi, mentre str_ref è un riferimento di rvalue a std::string , la categoria di valore dell'espressione str_ref non è un valore rvalore . È un'espressione lvalue. Sì davvero. Per questo str_ref , non è possibile chiamare il costruttore di move di std::string con l'espressione str_ref . La riga 6 copia quindi il valore di str in test3 .

Per spostarlo, dovremmo impiegare di nuovo std::move .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow