Ответ 1
std::reference_wrapper
полезен в сочетании с шаблонами. Он обертывает объект, сохраняя указатель на него, позволяя переназначать и копировать, имитируя обычную семантику. Он также инструктирует определенные шаблоны библиотеки хранить ссылки вместо объектов.
Рассмотрим алгоритмы в STL, которые копируют функторы: вы можете избежать этой копии, просто передав ссылочную оболочку со ссылкой на функтор вместо самого функтора:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine state
Это работает, потому что...
-
...
reference_wrapper
перегрузитьoperator()
, чтобы их можно было вызвать так же, как объекты функций, на которые они ссылаются:std::ref(myEngine)() // Valid expression, modifies myEngines state
-
... (un) как обычные ссылки, копирование (и присвоение)
reference_wrappers
просто назначает pointee.int i, j; auto r = std::ref(i); // r refers to i r = std::ref(j); // Okay; r refers to j r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
Копирование ссылочной обертки практически эквивалентно копированию указателя, который так же дешев, как и он. Все вызовы функций, присущие его использованию (например, те, что указаны в operator()
), должны быть просто встроены, поскольку они являются однострочными.
reference_wrapper
создаются с помощью std::ref
и std::cref
:
int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>
Аргумент шаблона указывает тип и cv-квалификацию упомянутого объекта; r2
относится к const int
и дает только ссылку на const int
. Вызовы для ссылочных оберток с функторами const
в них будут вызывать только const
функцию-член operator()
s.
Инициализаторы Rvalue не разрешены, так как это дает им больше вреда, чем пользы. Поскольку rvalues будет перемещаться в любом случае (и с гарантированным разрешением на копирование, даже если это частично избегается), мы не улучшаем семантику; мы можем ввести оборванные указатели, хотя, поскольку эталонная оболочка не продлевает срок жизни указателя.
Взаимодействие с библиотекой
Как упоминалось ранее, можно указать make_tuple
сохранить ссылку в результирующем tuple
, передав соответствующий аргумент через reference_wrapper
:
int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
// Type of t2 is tuple<int&>
Обратите внимание, что это немного отличается от forward_as_tuple
: Здесь rvalues как аргументы недопустимы.
std::bind
показывает такое же поведение: он не копирует аргумент, а сохраняет ссылку, если это reference_wrapper
. Полезно, если этот аргумент (или функтор!) Не нужно копировать, но остается в области действия, пока используется bind
-functor.
Отличие от обычных указателей
-
Дополнительного уровня синтаксической косвенности нет. Указатели должны быть разыменованы, чтобы получить lvalue к объекту, на который они ссылаются;
reference_wrapper
имеют неявный оператор преобразования и могут быть вызваны как объект, который они обертывают.int i; int& ref = std::ref(i); // Okay
-
reference_wrapper
s, в отличие от указателей, не имеют нулевого состояния. Они должны быть инициализированы ссылкой или другойreference_wrapper
.std::reference_wrapper<int> r; // Invalid
-
Сходство - это нечетная семантика копии: указатели и
reference_wrapper
могут быть переназначены.