WPROWADZENIE
ISOC ++ 11 (oficjalnie ISO / IEC 14882: 2011) to najnowsza wersja standardu języka programowania C ++. Zawiera kilka nowych funkcji i pojęć, na przykład:
- referencje wartości
- kategorie wartości wyrażeń xvalue, glvalue, prvalue
- przenieść semantykę
Jeśli chcielibyśmy zrozumieć koncepcje nowych kategorii wartości wyrażeń, musimy zdawać sobie sprawę z tego, że istnieją odwołania do wartości i wartości. Lepiej jest wiedzieć, że wartości można przekazywać do odwołań do wartości nie stałych.
int& r_i=7; // compile error
int&& rr_i=7; // OK
Zyskamy trochę intuicji pojęć kategorii wartości, cytując podrozdział zatytułowany „Wartości lvalu i wartości rvalu” z roboczego szkicu N3337 (najbardziej podobny szkic do opublikowanego standardu ISOC ++ 11).
3.10 Wartości lv i wartości rv [basic.lval]
1 Wyrażenia są podzielone na kategorie zgodnie z taksonomią na rycinie 1.
- Wartość (tak zwana historycznie, ponieważ wartości mogą pojawiać się po lewej stronie wyrażenia przypisania) oznacza funkcję lub obiekt. [Przykład: jeśli E jest wyrażeniem typu wskaźnikowego, to * E jest wyrażeniem wartościowym odnoszącym się do obiektu lub funkcji, na które wskazuje E. Jako kolejny przykład wynikiem wywołania funkcji, której typem zwracanym jest odwołanie do wartości, jest wartość. —Przykład]
- Wartość x (wartość „eXpiring”) odnosi się również do obiektu, zwykle pod koniec jego życia (na przykład, aby można było przenieść jego zasoby). Wartość x jest wynikiem pewnych rodzajów wyrażeń obejmujących odwołania do wartości (8.3.2). [Przykład: wynikiem wywołania funkcji, której typem zwracanym jest odwołanie do wartości jest wartość x. —Przykład]
- Glvalue (wartość „uogólniona”) to wartość lub wartość x.
- Wartość (tak zwana historycznie, ponieważ wartości mogą pojawiać się po prawej stronie wyrażenia przypisania) jest wartością x, obiektem
tymczasowym (12.2) lub jej podobiektem lub wartością, która nie jest
powiązana z obiektem.
- Prvalue („czysta” wartość) to wartość, która nie jest wartością x. [Przykład: Wywołanie funkcji, której typem zwracanym nie jest
odwołanie, jest prvalue. Wartość literału, takiego jak 12, 7,3e5, lub
prawda, jest również wartością wstępną. —Przykład]
Każde wyrażenie należy do dokładnie jednej z podstawowych klasyfikacji tej taksonomii: lvalue, xvalue lub prvalue. Ta właściwość wyrażenia nazywana jest jego kategorią wartości.
Nie jestem jednak pewien, czy ten podrozdział wystarcza do jasnego zrozumienia pojęć, ponieważ „zwykle” nie jest tak naprawdę ogólny, „pod koniec swojego życia” nie jest tak naprawdę konkretny, „uwzględnianie odniesień do wartości” nie jest do końca jasne, i „Przykład: Wynikiem wywołania funkcji, której typem zwracanym jest odwołanie do wartości, jest wartość x.” brzmi jak wąż gryzie ogonem.
KATEGORIE WARTOŚCI PODSTAWOWEJ
Każde wyrażenie należy do dokładnie jednej podstawowej kategorii wartości. Te kategorie wartości to kategorie lvalue, xvalue i prvalue.
wartości
Wyrażenie E należy do kategorii wartości tylko i tylko wtedy, gdy E odnosi się do jednostki, która JUŻ Miała tożsamość (adres, nazwę lub alias), która umożliwia jej dostęp poza 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;
}
wartości
Wyrażenie E należy do kategorii xvalue tylko wtedy, gdy jest
- wynik wywołania funkcji, niejawnie lub jawnie, której typem zwracanym jest odwołanie do wartości typu zwracanego obiektu, lub
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;
}
- rzutowanie na odwołanie wartości do typu obiektu, lub
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;
}
- wyrażenie dostępu do elementu klasy oznaczające element danych niestatycznych typu innego niż referencyjny, w którym wyrażenie obiektowe jest wartością x, lub
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;
}
- wyrażenie wskaźnik do elementu, w którym pierwszy operand jest wartością x, a drugi operand jest wskaźnikiem do elementu danych.
Zauważ, że efekt powyższych reguł jest taki, że nazwane odwołania wartości do obiektów są traktowane jako wartości lv, a nienazwane odniesienia wartości do obiektów są traktowane jak wartości x; referencje wartości do funkcji są traktowane jako wartości lv, niezależnie od tego, czy są nazwane czy nie.
#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;
}
wartości
Wyrażenie E należy do kategorii prvalue wtedy i tylko wtedy, gdy E nie należy ani do wartości lvalue, ani do kategorii 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;
}
KATEGORIE MIESZANEJ WARTOŚCI
Istnieją dwie dalsze ważne kategorie mieszanych wartości. Te kategorie wartości to kategorie wartości i wartości glvalue.
wartości
Wyrażenie E należy do kategorii wartości, tylko jeśli E należy do kategorii wartości lub do kategorii wartości.
Zauważ, że ta definicja oznacza, że wyrażenie E należy do kategorii wartości, tylko wtedy, gdy E odnosi się do encji, która nie miała żadnej tożsamości, która czyni ją dostępną poza E YET.
wartości glvalues
Wyrażenie E należy do kategorii glvalue wtedy i tylko wtedy, gdy E należy do kategorii lvalue lub do kategorii xvalue.
PRAKTYCZNA ZASADA
Scott Meyer opublikował bardzo przydatną ogólną zasadę odróżniającą wartości od wartości lv.
- Jeśli możesz wziąć adres wyrażenia, wyrażenie jest wartością.
- Jeśli typem wyrażenia jest odwołanie do wartości (np. T & lub const T & itp.), To wyrażenie jest wartością.
- W przeciwnym razie wyrażenie jest wartością. Pod względem koncepcyjnym (i zazwyczaj także w rzeczywistości) wartości rv odpowiadają obiektom tymczasowym, takim jak te zwracane z funkcji lub tworzone przez konwersje typu niejawnego. Większość wartości literalnych (np. 10 i 5,3) to również wartości.