Для обеспечения операции пересылки, новый стандарт вводит новый вид ссылок — ссылки на r-значение. Ссылка на r-значение (r-value reference) — это ссылка, которая должна быть связана с r-значением. Ссылку на r-значение получают с использованием символа &&, а не &. Как будет продемонстрировано далее, у ссылок на r-значение есть важное свойство — они могут быть связаны только с тем объектом, который будет удален. В результате можно "перемещать" ресурсы от ссылки на r-значение в другой объект.

Напомним, что l- и r-значение — свойства выражения (см. раздел 4.1.1). Некоторые выражения возвращают или требуют l-значений; другие возвращают или требуют r-значений. Как правило, выражение l-значения относится к идентификатору объекта, тогда как выражение r-значения — к значению объекта.

Как и любая ссылка, ссылка на r-значение — это только другое имя для объекта. Как известно, нельзя связать обычные ссылки (которые далее будем называть ссылками на l-значение (l-value reference), чтобы отличить их от ссылок на r-значения) с выражениями, требующими преобразования, с литералами и с выражениями, которые возвращают r-значение (см. раздел 2.3.1). У ссылок на r-значение противоположные свойства привязки: можно связать ссылку на r-значение с выражениями, приведенными выше, но нельзя непосредственно связать ссылку на r-значение с l-значением:

int i = 42;

int &r = i;   // ok: r ссылается на i

int &&rr = i; // ошибка: нельзя связать ссылку на r-значение

              // с l-значением

int &r2 = i * 42; // ошибка: i * 42 - это r-значение

const int &r3 = i * 42; // ok: ссылку на константу можно

                        // связать с r-значением

int &&rr2 = i * 42; // ok: связать rr2 с результатом умножения

Функции, возвращающие ссылки на l-значение, наряду с присвоением, индексированием, обращением к значению, а также префиксные операторы инкремента и декремента являются примерами выражений, возвращающих l-значения. Ссылку на l-значение можно также связать с результатом любого из этих выражений.

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

l-значения — устойчивы; r-значения — эфемерны

Глядя на список выражений l- и r-значений, становится понятно, что l- и r-значения существенно отличаются друг от друга: у l-значений есть постоянное состояние, тогда как r-значения, литералы и временные объекты создаются лишь в ходе вычисления выражений.

Поскольку ссылки на r-значение могут быть связаны только с временным объектом, известно, что:

• упомянутый объект будет удален,

• у этого объекта не может быть других пользователей.

Совместно эти факты означают, что использующий ссылку на r-значение код способен получать ресурсы от объекта, на который ссылается ссылка.

Ссылки на r-значение ссылаются на объекты, которые будут вскоре удалены. Следовательно, можно "захватить" состояние объекта, связанного со ссылкой на r-значение.

Переменные являются l-значениями

Хотя мы редко думаем об этом, переменная — это выражение с одним операндом и без оператора. Подобно любому другому выражению, переменная как выражение имеет свойства l- и r-значения. Переменные как выражения — это l-значения. Удивительно, но как следствие невозможно связать ссылку на r-значение с переменной, определенной как тип ссылки на r-значение:

int &&rr1 = 42;  // ok: литералы - это r-значения

int &&rr2 = rr1; // ошибка: выражение rr1 - это l-значение!

С учетом предыдущего наблюдения, согласно которому r-значения представляют эфемерные объекты, нет ничего удивительного в том, что переменная представляет собой l-значение. В конце концов, переменная сохраняется, пока не выйдет из области видимости.

Переменная — это l-значение; нельзя непосредственно связать ссылку на r-значение с переменной, даже если эта переменная была определена как тип ссылки на r-значение.

Библиотечная функция move()

Хотя нельзя непосредственно связать ссылку на r-значение с l-значением, можно явно привести l-значение к соответствующему типу ссылки на r-значение. Вызов новой библиотечной функции move(), определенной в заголовке utility, позволяет также получить ссылку на r-значение, привязанную к l-значению. Для возвращения ссылки на r-значение на данный объект функция move() использует средства, описываемые в разделе 16.2.6:

int &&rr3 = std::move(rr1); // ok

Перейти на страницу:

Похожие книги