Декларатор ссылки Rvalue: &&

Хранит ссылку на выражение rvalue.

type-id && cast-expression

Заметки

Ссылки Rvalue позволяют отличать lvalue из rvalue.Lvalue ссылки и ссылки rvalue синтаксически и семантически аналогичный, но они следуют различным правилам несколько.Дополнительные сведения о lvalues и rvalues см. в разделе Lvalues и Rvalues.Дополнительные сведения о ссылках lvalue см. в разделе Декларатор ссылки Lvalue: &.

В следующих разделах описывается ссылки rvalue поддерживают реализацию семантик перемещения и совершенного препровождения.

Семантика перемещения

Ссылки Rvalue поддерживают реализацию семантик перемещения, которая может значительно повысить производительность приложений.Семантика перемещения позволяет писать код, который динамически меняет местами ресурсов (например, выделенная память) от одного объекта к другому.Перемещение рабочих семантики, поскольку она включает ресурсы, которые необходимо передать из временных объектов, которые нельзя ссылаться в другом месте в программе.

Чтобы реализовать семантику перемещения, как правило, предоставляется конструктор перемещения и при необходимости, оператор присваивания движения (operator=), в создаваемый класс.Операции копирования источники и назначения, чтобы воспользоваться преимуществами rvalues затем автоматически семантик перемещения.В отличие от по умолчанию - конструктор копии, компилятор не предоставляет используемый по умолчанию конструктор перемещения.Дополнительные сведения о написании конструктор перемещения и способ его использования в приложении см. в разделе Как Разработайте конструктор перемещения.

Также можно перегружать обычные функции и операторы, чтобы воспользоваться преимуществами семантик перемещения.Visual C++ 2010 вставляет семантику перемещения в стандартной библиотеки шаблонов (STL).Например, класс string реализует семантику операций, которые выполняют перемещение.Рассмотрим следующий пример сцепляет несколько строк и выводит результат:

// string_concatenation.cpp
// compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

int main()
{
   string s = string("h") + "e" + "ll" + "o";
   cout << s << endl;
}

Перед Visual C++ 2010, каждый вызов operator+ выбирает и возвращает новый временный объект string (rvalue).operator+ не может добавлять по одной строке на другой, так как он не знает ли строки источника lvalues или rvalues.Если оба lvalues строки источника, то они могут ссылаться в другом месте в программе и поэтому не следует изменять.С помощью ссылки rvalue, operator+ можно изменить, чтобы принять rvalues, которые нельзя ссылаться в другом месте в программе.Поэтому operator+ теперь может добавить одну строку в другую.Это может значительно снизить количество динамических операций выделения памяти, класс string должен быть запущен.Дополнительные сведения о классе string см. в разделе basic_string Class.

Переместите справки семантики также, если компилятор не может использовать оптимизацию возвращаемого значения (RVO) или именованной оптимизацию возвращаемого значения (NRVO).В этих случаях компилятор вызывает конструктор перемещения, если тип определяет его.Дополнительные сведения об именованной оптимизации возвращаемого значения см. в разделе Именованная оптимизация возвращаемого значения в Visual C++ 2005.

Для лучшего понимания семантики перемещения рассмотрим пример вставка элемента в объект vector.Если емкость объекта vector превышается, то объект vector должен reallocate память для своих элементов, а затем скопировать каждый элемент в другое расположение в памяти, чтобы освободить место для вставленного элемента., Если операция вставки копирует элемент, он создает новый элемент, вызывает конструктор копировать, чтобы скопировать данные из предыдущего элемента к новому элементу, и затем удаляет предыдущий элемент.Семантика перемещения позволяет перемещать объекты напрямую без выполнения ресурсоемкие операции выделения памяти и копий.

Чтобы воспользоваться преимуществами семантик перемещения в примере vector, можно написать конструктор переходит к данным переходит от одного объекта к другому.

Дополнительные сведения о введении семантик перемещения в STL в Visual C++ 2010 см. в разделе Справочник по стандартной библиотеки C++.

Повышение препровождение

Совершенное препровождение уменьшает потребность в перегруженных функций и помогает избежать проблем препровождения.Препровождения проблема может возникнуть при написании универсальная функция, которая принимает ссылки, так как его параметры и передают эти параметры (или переадресует) в другую функцию.Например, если универсальная функция принимает параметр типа const T&, вызываемая функция не удается изменить значение этого параметра.Если универсальная функция принимает параметр типа T&, то функция не может быть называется с помощью rvalue (например, литерал временного объекта или целые числа).

Обычно решить эту проблему, необходимо предоставить универсальной перегруженные версии функций, принимающих и T& и const T& для каждого из его параметров.В результате число перегруженных функций растет экспоненциально с количеством параметров.Ссылки Rvalue позволяют создавать одну версию функции, принимающую произвольные аргументы и переадресует их в другой функции, если другая функция была называется напрямую.

Рассмотрим следующий пример, в котором объявляется 4 типов, W, X, Y и Z.Конструктор для каждого типа имеет другое сочетание const и ссылки const lvalue, в качестве параметров.

struct W
{
   W(int&, int&) {}
};

struct X
{
   X(const int&, int&) {}
};

struct Y
{
   Y(int&, const int&) {}
};

struct Z
{
   Z(const int&, const int&) {}
};

Предположим, требуется написать универсальная функция, которая создает объекты.В следующем примере показан один способ написания эта функция:

template <typename T, typename A1, typename A2>
T* factory(A1& a1, A2& a2)
{
   return new T(a1, a2);
}

В следующем примере демонстрируется допустимый вызов функции factory:

int a = 4, b = 5;
W* pw = factory<W>(a, b);

Однако следующий пример не содержит допустимого вызов функции factory поскольку factory принимает ссылки lvalue, изменяемых в качестве параметров, но вызываются с помощью rvalues:

Z* pz = factory<Z>(2, 2);

Обычно решить эту проблему, необходимо создать перегруженную версию функции factory для каждого сочетания A& и параметров const A&.Ссылки Rvalue позволяют создавать одну версию функции factory, как показано в следующем примере:

template <typename T, typename A1, typename A2>
T* factory(A1&& a1, A2&& a2)
{
   return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

В этом примере используются ссылки rvalue в качестве параметров в factory действуют.Назначение функции std::forward переадресованы параметры функции фабрики в конструктор класса шаблона.

В следующем примере показана функция main, которая использует откорректированную функцию factory для создания экземпляров W, X, Y и классов Z.Откорректированная функция factory переадресует его параметры (или lvalues или rvalues) к соответствующему конструктору классов.

int main()
{
   int a = 4, b = 5;
   W* pw = factory<W>(a, b);
   X* px = factory<X>(2, b);
   Y* py = factory<Y>(a, 2);
   Z* pz = factory<Z>(2, 2);

   delete pw;
   delete px;
   delete py;
   delete pz;
}

Дополнительные свойства ссылки Rvalue

Можно перегружать функцию для получения ссылки и ссылки rvalue lvalue.

Можно перегружать функцию для получения ссылки rvalue или lvalue const ссылку можно написать код, различаются немодифицируемыми объектами (lvalues) и изменяемые временными значениями (rvalues).Можно передать объект функции, принимающей ссылку rvalue если объект не помечен как const.В следующем примере показана функция f, которая перегружатьа для получения ссылки rvalue lvalue и ссылку.Вызовы функций fmain, как с lvalues, так и rvalue.

// reference-overload.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void f(const MemoryBlock&)
{
   cout << "In f(const MemoryBlock&). This version cannot modify the parameter." << endl;
}

void f(MemoryBlock&&)
{
   cout << "In f(MemoryBlock&&). This version can modify the parameter." << endl;
}

int main()
{
   MemoryBlock block;
   f(block);
   f(MemoryBlock());
}

В этом примере получается следующий результат:

In f(const MemoryBlock&). This version cannot modify the parameter.
In f(MemoryBlock&&). This version can modify the parameter.

В этом примере первый вызов f передает локальную переменную (lvalue) в качестве аргумента.Второй вызов f передается временный объект в качестве аргумента.Так как временный объект нельзя ссылаться в другом месте в программе, вызов привязывается к перегруженной версии f, которая принимает ссылки rvalue, которая может изменить объект.

Компилятор обрабатывает ссылки rvalue с именем как lvalue и безымянную rvalue в качестве ссылки rvalue.

При написании функция, которая принимает ссылки rvalue в качестве его параметра, этот параметр обрабатывается как lvalue в теле функции.Компилятор обрабатывает ссылки rvalue с именем как lvalue, так как именованный объект может быть реализован на несколько частей программы; unsafe было бы включить несколько частей программы, чтобы изменить или удалить ресурсы из этого объекта.Например, если несколько частей программы пытаются передать ресурсы одного и того же объекта, то только первая часть - это надежная передача успешно ресурс.

В следующем примере показана функция g, которая перегружатьа для получения ссылки rvalue lvalue и ссылку.Функция f принимает значение rvalue в качестве своего параметра ссылку именованной ссылка rvalue и передачи ссылки rvalue (безымянная ссылка rvalue).При вызове метода g из f разрешение перегрузки выбирает версию g, принимающей ссылку lvalue, так как тело f рассматривает его как параметр lvalue.При вызове метода g из main разрешение перегрузки выбирает версию g, которая принимает ссылки rvalue поскольку f возвращает ссылку rvalue.

// named-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

MemoryBlock&& f(MemoryBlock&& block)
{
   g(block);
   return block;
}

int main()
{
   g(f(MemoryBlock()));
}

В этом примере получается следующий результат:

In g(const MemoryBlock&).
In g(MemoryBlock&&).

В этом примере функция main передает значение rvalue к f.Текст сообщения f рассматривает его как именованный параметр lvalue.Вызов от f к g привязывает параметр по ссылке lvalue (первой перегруженной версии g).

  • Можно привести lvalue для ссылки rvalue.

Функция STL std::move позволяет преобразовать объект для ссылки rvalue к этому объекту.Кроме того, можно использовать ключевое слово static_cast для приведения lvalue для ссылки rvalue, как показано в следующем примере:

// cast-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

int main()
{
   MemoryBlock block;
   g(block);
   g(static_cast<MemoryBlock&&>(block));
}

В этом примере получается следующий результат:

In g(const MemoryBlock&).
In g(MemoryBlock&&).

 

Шаблоны функции дедуцируют них типы аргументов шаблона и затем использовать ссылку свернуть правила.

Он применяется для написания шаблона функции, передающий (или переадресует) его параметры в другую функцию.Важно понять, как работает вычет типа шаблона для шаблонов функции, принимающие ссылки rvalue.

Если аргумент функции rvalue, компилятор выводит аргумент будет ссылка rvalue.Например, при передаче ссылки rvalue к объекту типа X к функции шаблона, что тип T&& взятия качестве своего параметра, вычет аргумента шаблона выводит T чтобы быть X.Поэтому параметр имеет тип X&&.Если аргумент функции lvalue или lvalue const, компилятор выводит его тип, чтобы быть ссылкой или lvalue по ссылке lvalue const этого типа.

В следующем примере объявляется один шаблон макета, а затем специализирует его для различных ссылочных типов.Ссылка rvalue print_type_and_value функция принимает в качестве параметра, и переадресует ее в соответствующую специализированной версии метода S::print.Функция main показаны различные способы вызова метода S::print.

// template-type-deduction.cpp
// Compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

template<typename T> struct S;

// The following structures specialize S by 
// lvalue reference (T&), const lvalue reference (const T&), 
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of 
// the structure and its parameter.

template<typename T> struct S<T&> {
   static void print(T& t)
   {
      cout << "print<T&>: " << t << endl;
   }
};

template<typename T> struct S<const T&> {
   static void print(const T& t)
   {
      cout << "print<const T&>: " << t << endl;
   }
};

template<typename T> struct S<T&&> {
   static void print(T&& t)
   {
      cout << "print<T&&>: " << t << endl;
   }
};

template<typename T> struct S<const T&&> {
   static void print(const T&& t)
   {
      cout << "print<const T&&>: " << t << endl;
   }
};

// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t) 
{
   S<T&&>::print(std::forward<T>(t));
}

// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }

int main()
{
   // The following call resolves to:
   // print_type_and_value<string&>(string& && t)
   // Which collapses to:
   // print_type_and_value<string&>(string& t)
   string s1("first");
   print_type_and_value(s1); 

   // The following call resolves to:
   // print_type_and_value<const string&>(const string& && t)
   // Which collapses to:
   // print_type_and_value<const string&>(const string& t)
   const string s2("second");
   print_type_and_value(s2);

   // The following call resolves to:
   // print_type_and_value<string&&>(string&& t)
   print_type_and_value(string("third"));

   // The following call resolves to:
   // print_type_and_value<const string&&>(const string&& t)
   print_type_and_value(fourth());
}

В этом примере получается следующий результат:

print<T&>: first
print<const T&>: second
print<T&&>: third
print<const T&&>: fourth

Чтобы включить каждый вызов функции print_type_and_value компилятор сначала выполняет вычет аргумента шаблона.Компилятор применяет ссылку свернуть правила, когда он подставляет дедуцированные аргументы шаблона для параметров типа.Например, значение локальной переменной s1 функции print_type_and_value указывает компилятору создавать следующую функцию сигнатуру:

print_type_and_value<string&>(string& && t)

Правила использования компилятора ссылаются на свернуть уменьшить ее к следующему:

print_type_and_value<string&>(string& t)

Эта версия функции print_type_and_value затем переадресует параметр в правильную специализированной версии метода S::print.

Следующая таблица обобщает ссылку свернуть правила для вычета типа аргумента шаблона:

Разрезанная тип

Свернутый тип

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

Вычет аргумента шаблона важный элемент реализация совершенное препровождение.Препровождение раздела совершенное, которое ранее в этом разделе, совершенный переадресованы описаны более подробно.

Сводка

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

См. также

Задачи

Как Разработайте конструктор перемещения

Ссылки

Выражения с унарными операторами

Декларатор ссылки Lvalue: &

Lvalues и Rvalues

move

forward

Другие ресурсы

Справочник по стандартной библиотеки C++