Разница между std:: reference wrapper и простым указателем?
зачем нужно иметь std::reference_wrapper? Где его следует использовать? Чем он отличается от простого указателя? Как его производительность сравнивается с простым указателем?
4 ответов:
std::reference_wrapperполезно в сочетании с шаблонами. Он обертывает объект, сохраняя указатель на него, что позволяет переназначать и копировать, имитируя его обычную семантику. Он также предписывает определенным шаблонам библиотек хранить ссылки вместо объектов.рассмотрим алгоритмы в STL, которые копируют функторы: вы можете избежать этой копии, просто передав ссылочную оболочку, ссылающуюся на функтор, а не на сам функтор:
unsigned arr[10]; std::mt19937 myEngine; std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's stateэто работает потому что...
...
reference_wrappers перегрузкаoperator()таким образом, они могут быть вызваны так же, как объекты функции, на которые они ссылаются:std::ref(myEngine)() // Valid expression, modifies myEngines state...(un)как обычные ссылки, копирование (и назначение)
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_wrappers создаются через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 будет перемещен в любом случае (и с гарантированная копия 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: здесь значения rvalues в качестве аргументов не допускаются.
std::bindпоказывает то же самое поведение: он не будет копировать аргумент, но хранить ссылку, если этоreference_wrapper. Полезно, если этот аргумент (или функтор!) не нужно копировать, но остается в области действия, покаbind-используется функтор.разница из обычных указателей
нет дополнительного уровня синтаксической абстракции. Указатели должны быть разыменованы, чтобы получить значение lvalue для объекта, на который они ссылаются;
reference_wrappers имеют неявное оператор преобразования и может быть вызван как объект, который они обертывают.int i; int& ref = std::ref(i); // Okay
reference_wrappers, в отличие от указателей, не имеют нулевого состояния. Они должны быть инициализированы с помощью либо ссылку или ещеreference_wrapper.std::reference_wrapper<int> r; // Invalidсходство-это мелкая семантика копирования: указатели и
reference_wrappers можно переназначить.
есть, по крайней мере, две мотивирующие цели
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(). Или используйте указатель.поскольку он добавляет дополнительный слой косвенности, он будет строго хуже (но, вероятно, незначительно), чем
T&в случаях, когда оба могут быть использованы, так что вы должны использовать его только когда это действительно необходимо.другие причуды:
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
Comments