﻿# Категории выражений и виды ссылок

Каждое выражение в языке C\+\+ имеет два свойства – тип и категорию\. В зависимости от категории выражения, с ним может быть связана ссылка определенного типа\.

## Категории выражения

До поддержки семантики перемещения в языке C\+\+ были две категории выражений – lvalue и rvalue\. Чтобы выяснить, является ли конкретное выражение lvalue или rvalue, использовалось следующее интуитивное правило: если выражение может стоять в левой части присваивания, то оно lvalue, иначе оно rvalue\.

Начиная со стандарта C\+\+11, для поддержки семантики перемещения были введены новые категории значений – glvalue, prvalue и xvalue\. Теперь любое выражение языка может принадлежать одной из этих категорий\.

Категория rvalue является объединением prvalue и xvalue, а категория glvalue – объединением lvalue и xvalue\.

Формального определения категорий в явном виде в стандарте С\+\+ нет\. В документе для каждого типа выражения указана категория выражений, которой он принадлежит\. На практике же удобно использовать следующие интуитивные определения категорий lvalue, xvalue или prvalue:

* Выражение принадлежит категории prvalue, если это выражение соответствует временному неименованному объекту\. Например, _i\+\+_\.
* Выражение принадлежит категории xvalue, если это именованный объект, ресурсы которого могут быть переиспользованы\. Это может быть результат выражения _static\_cast<type &&\>_ или результат вызова функции, возвращающей _type &&_\. Например, _std::move\(obj\)_\.
* Все остальные выражения принадлежат категории lvalue\. Например, _\*ptr_\.

## Виды ссылок

До С\+\+11 существовал только один вид ссылок\. Если мы хотели взять ссылку на переменную _var_ типа _type_, то следовало писать следующее:

```cpp
type &ref = var;
```

Такая ссылка называется lvalue\-ссылка\. Она может быть привязана только к lvalue\-выражению\.

Для поддержки семантики перемещения были введены новые типы ссылок – rvalue\-ссылка и forwarding\-ссылка\. Если требуется rvalue\-ссылка, необходимо написать следующее:

```cpp
type &&ref = rvalue_expr;
```

Такая ссылка, в отличие от lvalue\-ссылки, должна ссылаться на rvalue\-выражение\.

Также была введена forwarding\-ссылка:

```cpp
template <typename T>
void foo(T &&arg);
```

где _T_ – это шаблонный параметр, выводимый из аргумента шаблона функции\. Такое объявление очень похоже на объявление rvalue\-ссылки, но работает иначе\. За счет механизма схлопывания ссылок forwarding\-ссылка может связываться как с lvalue, так и rvalue\-объектами\. Подробно этот механизм разобран [здесь](https://pvs-studio.ru/ru/blog/terms/6515/)\.

Не стоит путать forwarding и rvalue ссылки\. В примере ниже шаблонный аргумент _T_ относится к классу _Base_, а не к функции _foo_\. Поэтому аргумент _arg_ функции _foo_ является не forwarding, а rvalue\-ссылкой:

```cpp
template <typename T>
class Base
{
  void foo(T&& arg);
};
```

Наличие в языке forwarding\-ссылки позволяет реализовать механизм идеальной передачи \(perfect forwarding\)\. Суть этого механизма в том, что в зависимости от своего типа объект передается так, как это ожидается: rvalue\-объект будет перемещен, lvalue\-объект будет скопирован\. Реализацией perfect forwarding в стандартной библиотеке является [функция _std::forward_](https://pvs-studio.ru/ru/blog/terms/6515/)\.