Зачем это нужно std::reference_wrapper
? Где его использовать? Чем он отличается от простого указателя? Как его производительность по сравнению с простым указателем?
100
Зачем это нужно std::reference_wrapper
? Где его использовать? Чем он отличается от простого указателя? Как его производительность по сравнению с простым указателем?
.
вместо->
.
не работает так, как вы предлагаете (если в какой-то момент предложение точки оператора не будет принято и интегрировано :))get()
функцией-членом или с его неявным преобразованием обратно в базовый тип.Ответы:
std::reference_wrapper
полезно в сочетании с шаблонами. Он обертывает объект, сохраняя указатель на него, позволяя переназначать и копировать, имитируя его обычную семантику. Он также указывает некоторым шаблонам библиотеки хранить ссылки вместо объектов.Рассмотрим алгоритмы в STL, которые копируют функторы: вы можете избежать этой копии, просто передав ссылочную оболочку, ссылающуюся на функтор, а не на сам функтор:
Это работает, потому что…
…
reference_wrapper
S перегрузка,operator()
чтобы их можно было вызывать точно так же, как объекты функций, на которые они ссылаются:… (Не) как обычные ссылки, копирование (и присвоение)
reference_wrappers
просто присваивает указатель.Копирование ссылочной оболочки практически эквивалентно копированию указателя, которое настолько дешево, насколько это возможно. Все вызовы функций, присущие его использованию (например, те, которые используются
operator()
), должны быть просто встроены, поскольку они являются однострочными.reference_wrapper
s создаются черезstd::ref
иstd::cref
:Аргумент шаблона определяет тип и квалификацию объекта, на который имеется ссылка;
r2
ссылается на aconst int
и дает только ссылку наconst int
. Вызовы ссылочных оболочек сconst
функторами в них будут вызывать толькоconst
функции-членыoperator()
.Инициализаторы Rvalue запрещены, поскольку их разрешение принесет больше вреда, чем пользы. Поскольку rvalues все равно будут перемещены (и с гарантированным исключением копирования, даже если этого частично избежать), мы не улучшаем семантику; мы можем ввести висячие указатели, поскольку обертка ссылок не продлевает время жизни указателя.
Библиотечное взаимодействие
Как упоминалось ранее, можно
make_tuple
указать сохранить ссылку в результатеtuple
, передав соответствующий аргумент черезreference_wrapper
:Обратите внимание, что это немного отличается от
forward_as_tuple
: Здесь rvalues в качестве аргументов не допускаются.std::bind
показывает то же поведение: он не копирует аргумент, но сохраняет ссылку, если этоreference_wrapper
. Полезно, если этот аргумент (или функтор!) Не нужно копировать, но остается в области видимости, пока используетсяbind
-функтор.Отличие от обычных указателей
Дополнительного уровня синтаксической косвенности нет. Указатели должны быть разыменованы, чтобы получить l-значение для объекта, на который они ссылаются;
reference_wrapper
s имеют неявный оператор преобразования и могут вызываться как объект, который они обертывают.reference_wrapper
s, в отличие от указателей, не имеет нулевого состояния. Они должны быть инициализированы ссылкой или другойreference_wrapper
.Сходство
reference_wrapper
заключается в семантике неглубокого копирования: указатели и s можно переназначать.источник
std::make_tuple(std::ref(i));
превосходитstd::make_tuple(&i);
?i
, а не ссылку на него.Есть как минимум две мотивирующие цели
std::reference_wrapper<T>
:Он должен дать ссылочную семантику объектам, передаваемым в качестве параметра значения в шаблоны функций. Например, у вас может быть большой функциональный объект, который вы хотите передать,
std::for_each()
который принимает параметр своего функционального объекта по значению. Чтобы избежать копирования объекта, вы можете использоватьПередача аргументов в
std::reference_wrapper<T>
кstd::bind()
выражению довольно часто , чтобы связать аргументы по ссылке , а не по значению.При использовании
std::reference_wrapper<T>
сstd::make_tuple()
соответствующим элементом кортежа становится а,T&
а не аT
:источник
fun
, это объект функции (т. е. объект класса с оператором вызова функции), а не функция: еслиfun
это фактическая функция, неstd::ref(fun)
иметь цели и потенциально замедлять выполнение кода.Еще одно отличие, с точки зрения самодокументируемого кода, заключается в том, что использование
reference_wrapper
объекта фактически отменяет право собственности на объект. Напротив, aunique_ptr
заявляет о владении, в то время как пустой указатель может принадлежать или не принадлежать (это невозможно узнать, не глядя на множество связанного кода):источник
reference_wrapper
превосходит необработанные указатели не только потому, что ясно, что они не принадлежат, но и потому, что это не может бытьnullptr
(без махинаций), и, следовательно, пользователи знают, что они не могут пройтиnullptr
(без махинаций), и вы знаете, что вам не нужно проверьте это.Вы можете думать об этом как о удобной оболочке вокруг ссылок, чтобы вы могли использовать их в контейнерах.
По сути, это
CopyAssignable
версияT&
. Каждый раз, когда вам нужна ссылка, но она должна быть назначаемой, использоватьstd::reference_wrapper<T>
или ее вспомогательную функциюstd::ref()
. Или используйте указатель.Другие причуды
sizeof
::И сравнение:
источник
reference_wrapper
код, сделав его идентичным коду, который использует указатель или ссылку.std::reference_wrapper
имеет гарантию, что объект никогда не будет нулевым. Рассмотрим члена классаstd::vector<T *>
. Вы должны изучить весь код класса, чтобы увидеть, может ли этот объект когда-либо хранить anullptr
в векторе, тогда как сstd::reference_wrapper<T>
, у вас гарантированно будут действительные объекты.