Почему нужно иметь std::reference_wrapper
? Где это должно быть использовано? Чем он отличается от простого указателя? Как его производительность сравнивается с простым указателем?
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>
Аргумент шаблона определяет тип и cv-квалификацию объекта, на который ссылаются; r2
относится к const int
и будет давать только ссылку на const int
, Призывает ссылаться на обертки с const
Функторы в них будут вызывать только const
функция-член operator()
s.
Инициализаторы Rvalue запрещены, поскольку их разрешение принесет больше вреда, чем пользы. Так как значения будут перемещены в любом случае (и с гарантированная копия elision даже это частично избегается), мы не улучшаем семантику; мы можем ввести висячие указатели, так как эталонная оболочка не продлевает срок жизни пуантиста.
Как упоминалось ранее, можно проинструктировать 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
Здесь значения в качестве аргументов недопустимы.
std::bind
показывает то же поведение: он не будет копировать аргумент, но будет хранить ссылку, если это reference_wrapper
, Полезно, если этот аргумент (или функтор!) Не нужно копировать, но он остается в области действия, пока bind
-функционер используется.
Дополнительного уровня синтаксической косвенности нет. Указатели должны быть разыменованы, чтобы получить lvalue к объекту, на который они ссылаются; reference_wrapper
с неявным оператор преобразования и может быть назван как объект, который они обертывают.
int i;
int& ref = std::ref(i); // Okay
reference_wrapper
s, в отличие от указателей, не имеют нулевого состояния. Они должны быть инициализированы с либо ссылка, либо другое reference_wrapper
.
std::reference_wrapper<int> r; // Invalid
Сходство семантики мелкого копирования: указатели и reference_wrapper
s может быть переназначен.
Есть, по крайней мере, две мотивирующие цели std::reference_wrapper<T>
:
Он предназначен для задания семантики ссылок на объекты, передаваемые в качестве параметра-значения шаблонам функций. Например, у вас может быть большой функциональный объект, который вы хотите передать std::for_each()
который принимает свой параметр объекта функции по значению. Чтобы избежать копирования объекта, вы можете использовать
std::for_each(begin, end, std::ref(fun));
Передавая аргументы как std::reference_wrapper<T>
для std::bind()
выражение довольно часто связывает аргументы по ссылке, а не по значению.
При использовании std::reference_wrapper<T>
с std::make_tuple()
соответствующий элемент кортежа становится T&
а не T
:
T object;
f(std::make_tuple(1, std::ref(object)));
Другое отличие, с точки зрения самодокументируемого кода, состоит в том, что использование reference_wrapper
по существу дезавуирует владение объектом. В отличие от unique_ptr
утверждает владение, в то время как голый указатель может или не может принадлежать (это невозможно узнать, не глядя на большое количество связанного кода):
vector<int*> a; // the int values might or might not be owned
vector<unique_ptr<int>> b; // the int values are definitely owned
vector<reference_wrapper<int>> c; // the int values are definitely not owned
Вы можете думать об этом как об удобной обертке вокруг ссылок, чтобы вы могли использовать их в контейнерах.
std::vector<std::reference_wrapper<T>> vec; // OK - does what you want
std::vector<T&> vec2; // Nope! Will not compile
Это в основном CopyAssignable
версия T&
, В любое время, когда вы хотите ссылку, но она должна быть назначаемой, используйте std::reference_wrapper<T>
или его вспомогательная функция std::ref()
, Или используйте указатель.
Другие причуды: sizeof
:
sizeof(std::reference_wrapper<T>) == sizeof(T*) // so 8 on a 64-bit box
sizeof(T&) == sizeof(T) // so, e.g., sizeof(vector<int>&) == 24
И сравнение:
int i = 42;
assert(std::ref(i) == std::ref(i)); // ok
std::string s = "hello";
assert(std::ref(s) == std::ref(s)); // compile error