Поиск…


Значение Категория Значение

Выражениям на C ++ присваивается определенная категория значений, основанная на результате этих выражений. Категории значений для выражений могут влиять на перегрузку функции C ++.

Категории значений определяют два важных, но отдельных свойства выражения. Одним из свойств является то, имеет ли выражение тождество. Выражение имеет личность, если оно относится к объекту с именем переменной. Имя переменной не может быть задействовано в выражении, но объект все еще может иметь его.

Другое свойство заключается в том, является ли законным неявное перемещение из значения выражения. Или, более конкретно, будет ли выражение при использовании в качестве параметра функции связываться с параметрами параметров r-значения или нет.

C ++ определяет 3 категории значений, которые представляют полезную комбинацию этих свойств: lvalue (выражения с идентичностью, но не движимые), xvalue (выражения с идентификацией, которые могут быть перемещены), и prvalue (выражения без идентификатора, которые можно перемещать). C ++ не имеет выражений, которые не имеют идентификатора и не могут быть перемещены.

C ++ определяет две другие категории значений, каждая из которых основана исключительно на одном из этих свойств: glvalue (выражения с идентификатором) и rvalue (выражения, которые можно перенести из). Они действуют как полезные группировки предыдущих категорий.

Этот график служит иллюстрацией:

График категорий значений на языке C ++

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.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow