ВВЕДЕНИЕ
ISOC ++ 11 (официально ISO / IEC 14882: 2011) является самой последней версией стандарта языка программирования C ++. Он содержит некоторые новые функции и концепции, например:
- Rvalue ссылки
- xvalue, glvalue, prvalue выражения значения категории
- переместить семантику
Если мы хотим понять концепцию новых категорий значений выражений, мы должны знать, что существуют ссылки на rvalue и lvalue. Лучше знать, что значения могут быть переданы неконстантным ссылкам.
int& r_i=7; // compile error
int&& rr_i=7; // OK
Мы можем получить некоторое представление о концепциях категорий значений, если процитируем подраздел «Значения и значения» из рабочего проекта N3337 (наиболее похожего на опубликованный стандарт ISOC ++ 11).
3.10 Lvalues и rvalues [basic.lval]
1 Выражения классифицированы в соответствии с таксономией на рисунке 1.
- Lvalue (исторически так называемое lvalue, которое может появляться в левой части выражения присваивания) обозначает функцию или объект. [Пример: если E является выражением типа указателя, то * E является выражением lvalue, ссылающимся на объект или функцию, на которые указывает E. В качестве другого примера, результатом вызова функции, тип возвращаемой которой является ссылкой на lvalue, является lvalue. - конец примера]
- Значение xvalue (значение «eXpiring») также относится к объекту, обычно ближе к концу его времени жизни (например, чтобы его ресурсы могли быть перемещены). Xvalue - это результат некоторых видов выражений, включающих ссылки на rvalue (8.3.2). [Пример: результатом вызова функции, тип возвращаемой которой является ссылкой на rvalue, является значение xvalue. - конец примера]
- Glvalue («обобщенное» lvalue) - это lvalue или xvalue.
- Значение r (исторически так называемое, поскольку значения r могут появляться в правой части выражения присваивания) представляет собой значение x,
временный объект (12.2) или его подобъект или значение, которое не
связано с объектом.
- Prvalue («чистое» rvalue) - это rvalue, которое не является xvalue. [Пример: результат вызова функции, тип возвращаемой которой не является
ссылкой, является предварительным значением. Значение литерала, такого как 12, 7.3e5 или
true, также является prvalue. - конец примера]
Каждое выражение принадлежит ровно к одной из основных классификаций в этой таксономии: lvalue, xvalue или prvalue. Это свойство выражения называется его категорией значений.
Но я не совсем уверен в том, что этого подраздела достаточно для четкого понимания концепций, потому что «обычно» не является действительно общим, «ближе к концу его жизненного цикла» не совсем конкретно, «использование ссылок на значения» не совсем понятно, и «Пример. Результатом вызова функции, тип возвращаемой которой является ссылкой на rvalue, является значение xvalue». Похоже, змея кусает свой хвост.
ПЕРВИЧНАЯ ЦЕННОСТЬ КАТЕГОРИИ
Каждое выражение принадлежит ровно одной категории первичных значений. Этими категориями значений являются категории lvalue, xvalue и prvalue.
lvalues
Выражение E относится к категории lvalue тогда и только тогда, когда E относится к объекту, у которого УЖЕ есть идентификатор (адрес, имя или псевдоним), который делает его доступным вне 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;
}
xvalues
Выражение E относится к категории xvalue тогда и только тогда, когда оно
- результат вызова функции, неявной или явной, чей тип возвращаемого значения является ссылкой на тип возвращаемого объекта, или
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;
}
- приведение к rvalue-ссылке на тип объекта, или
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;
}
- выражение доступа к члену класса, обозначающее нестатический член данных не ссылочного типа, в котором объектное выражение является значением xvalue, или
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;
}
- выражение указателя на член, в котором первый операнд является значением x, а второй операнд - указателем на элемент данных.
Обратите внимание, что эффект приведенных выше правил заключается в том, что именованные ссылки на значения rvalue обрабатываются как lvalues, а безымянные ссылки на значения rvalue на объекты обрабатываются как xvalues; rvalue ссылки на функции обрабатываются как lvalues независимо от того, названы они или нет.
#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;
}
prvalues
Выражение E относится к категории prvalue тогда и только тогда, когда E не принадлежит ни к lvalue, ни к категории 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;
}
КАТЕГОРИИ СМЕШАННЫХ ЦЕННОСТЕЙ
Есть еще две важные категории смешанных значений. Этими категориями значений являются категории rvalue и glvalue.
rvalues
Выражение E принадлежит категории rvalue тогда и только тогда, когда E принадлежит категории xvalue или категории prvalue.
Обратите внимание, что это определение означает, что выражение E принадлежит категории rvalue тогда и только тогда, когда E относится к объекту, у которого нет какой-либо идентичности, которая делает его доступным вне E YET.
glvalues
Выражение E принадлежит категории glvalue тогда и только тогда, когда E принадлежит категории lvalue или категории xvalue.
ПРАКТИЧЕСКОЕ ПРАВИЛО
Скотт Мейер опубликовал очень полезное практическое правило, позволяющее отличать значения от значений.
- Если вы можете взять адрес выражения, выражение будет lvalue.
- Если тип выражения является ссылкой на lvalue (например, T & или const T & и т. Д.), Это выражение является lvalue.
- В противном случае выражение является значением. Концептуально (и обычно также фактически) значения соответствуют временным объектам, таким как возвращаемые из функций или созданные неявными преобразованиями типов. Большинство литеральных значений (например, 10 и 5.3) также являются r-значениями.