C++
Категории ценностей
Поиск…
Значение Категория Значение
Выражениям на C ++ присваивается определенная категория значений, основанная на результате этих выражений. Категории значений для выражений могут влиять на перегрузку функции C ++.
Категории значений определяют два важных, но отдельных свойства выражения. Одним из свойств является то, имеет ли выражение тождество. Выражение имеет личность, если оно относится к объекту с именем переменной. Имя переменной не может быть задействовано в выражении, но объект все еще может иметь его.
Другое свойство заключается в том, является ли законным неявное перемещение из значения выражения. Или, более конкретно, будет ли выражение при использовании в качестве параметра функции связываться с параметрами параметров r-значения или нет.
C ++ определяет 3 категории значений, которые представляют полезную комбинацию этих свойств: lvalue (выражения с идентичностью, но не движимые), xvalue (выражения с идентификацией, которые могут быть перемещены), и prvalue (выражения без идентификатора, которые можно перемещать). C ++ не имеет выражений, которые не имеют идентификатора и не могут быть перемещены.
C ++ определяет две другие категории значений, каждая из которых основана исключительно на одном из этих свойств: glvalue (выражения с идентификатором) и rvalue (выражения, которые можно перенести из). Они действуют как полезные группировки предыдущих категорий.
Этот график служит иллюстрацией:
prvalue
Выражение prvalue (pure-rvalue) - это выражение, которое не имеет идентичности, оценка которого обычно используется для инициализации объекта и из которого можно неявно перемещать. К ним относятся, но не ограничиваются:
- Выражения, представляющие временные объекты, такие как
std::string("123")
. - Выражение вызова функции, которое не возвращает ссылку
- Литерал ( кроме строкового литерала - это lvalues), например,
1
,true
,0.5f
или'a'
- Лямбда-выражение
Встроенный адрес оператора ( &
) не может применяться к этим выражениям.
xvalue
Выражение xvalue (eXpiring value) - это выражение, которое имеет идентификатор и представляет объект, из которого можно неявно перемещать. Общая идея с выражениями xvalue заключается в том, что объект, который они представляют, скоро будет уничтожен (следовательно, часть «eXpiring») и, следовательно, неявно перемещается от них в порядке.
Дано:
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 является выражением, которое имеет идентичность, но не может быть неявным образом перемещено. Среди них - выражения, которые состоят из имени переменной, имени функции, выражений, которые используются встроенным оператором разыменования и выражений, которые ссылаются на ссылки lvalue.
Типичная lvalue - это просто имя, но lvalues могут появляться и в других вкусах:
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
Кроме того, в то время как большинство литералов (например, 4
, 'x'
и т. Д.) Являются prvalues, строковые литералы являются lvalues.
glvalue
Выражение glvalue («обобщенное значение lvalue») представляет собой любое выражение, имеющее идентичность, независимо от того, может ли оно быть перемещено или нет. Эта категория включает lvalues (выражения, которые имеют идентичность, но не могут быть перемещены) и xvalues (выражения, которые имеют идентификатор и могут быть перемещены из), но исключают prvalues (выражения без идентификатора).
Если выражение имеет имя , это значение glvalue:
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
Выражение rvalue - любое выражение, которое может быть неявно перемещено, независимо от того, имеет ли он идентификатор.
Более точно, выражения rvalue могут использоваться как аргумент функции, которая принимает параметр типа T &&
(где T
- тип expr
). В качестве аргументов для таких параметров функции могут указываться только выражения rvalue; если используется выражение non-rvalue, то разрешение перегрузки будет выбирать любую функцию, которая не использует параметр ссылки rvalue. И если они не существуют, вы получите сообщение об ошибке.
Категория выражений rvalue включает все выражения xvalue и prvalue и только те выражения.
Стандартная функция библиотеки std::move
существует, чтобы явно преобразовать выражение non-rvalue в r-значение. Более конкретно, оно превращает выражение в значение x, поскольку, даже если раньше оно было выражением без знака, передавая его как параметр для std::move
, оно получает идентификатор (имя параметра функции) и становится значением xvalue.
Рассмотрим следующее:
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
имеет конструктор, который принимает единственный параметр типа std::string&&
, обычно называемый «конструктором перемещения». Однако категория значений выражения str
не является rvalue (в частности, это lvalue), поэтому она не может вызвать эту перегрузку конструктора. Вместо этого он вызывает const std::string&
overload, конструктор копирования.
Строка 3 меняет ситуацию. Возвращаемое значение std::move
- это T&&
, где T
- базовый тип переданного параметра. Таким образом std::move(str)
возвращает std::string&&
. Вызов функции, возвращающее значение, является ссылкой rvalue, является выражением rvalue (в частности, значением xvalue), поэтому он может вызывать конструктор перемещения std::string
. После строки 3 str
была перенесена с (содержимое пользователя теперь не определено).
Строка 4 передает временный оператор присваивания std::string
. Это имеет перегрузку, которая принимает std::string&&
. Выражение std::string("new value")
является выражением rvalue (в частности, значением prvalue), поэтому оно может вызывать перегрузку. Таким образом, временное перемещается в str
, заменяя неопределенное содержимое определенным содержимым.
Строка 5 создает ссылку с именем rvalue, называемую str_ref
которая ссылается на str
. Вот почему категории ценностей запутываются.
См., В то время как str_ref
является ссылкой rvalue на std::string
, категория значений выражения str_ref
не является значением rvalue . Это выражение lvalue. Да, действительно. Из-за этого нельзя вызвать конструктор перемещения std::string
с выражением str_ref
. Поэтому строка 6 копирует значение str
в test3
.
Чтобы переместить его, нам пришлось бы использовать std::move
again.