std::reference_wrapper
полезно в сочетании с шаблонами. Он обертывает объект, сохраняя указатель на него, позволяя переназначать и копировать, имитируя его обычную семантику. Он также указывает некоторым шаблонам библиотеки хранить ссылки вместо объектов.
Рассмотрим алгоритмы в STL, которые копируют функторы: вы можете избежать этой копии, просто передав ссылочную оболочку, ссылающуюся на функтор, а не на сам функтор:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
Это работает, потому что…
… reference_wrapper
S перегрузка,operator()
чтобы их можно было вызывать точно так же, как объекты функций, на которые они ссылаются:
std::ref(myEngine)() // Valid expression, modifies myEngines state
… (Не) как обычные ссылки, копирование (и присвоение) reference_wrappers
просто присваивает указатель.
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
s создаются через 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>
Аргумент шаблона определяет тип и квалификацию объекта, на который имеется ссылка; r2
ссылается на a const int
и дает только ссылку на const int
. Вызовы ссылочных оболочек с const
функторами в них будут вызывать только const
функции-члены operator()
.
Инициализаторы 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
-функтор.
Отличие от обычных указателей
Дополнительного уровня синтаксической косвенности нет. Указатели должны быть разыменованы, чтобы получить l-значение для объекта, на который они ссылаются; reference_wrapper
s имеют неявный оператор преобразования и могут вызываться как объект, который они обертывают.
int i;
int& ref = std::ref(i); // Okay
reference_wrapper
s, в отличие от указателей, не имеет нулевого состояния. Они должны быть инициализированы ссылкой или другойreference_wrapper
.
std::reference_wrapper<int> r; // Invalid
Сходство reference_wrapper
заключается в семантике неглубокого копирования: указатели и s можно переназначать.
.
вместо->