Разница между std :: reference_wrapper и простым указателем?

Почему необходимо std::reference_wrapper ? Где это должно быть использовано? Чем он отличается от простого указателя? Как его производительность сравнивается с простым указателем?

68 голосов | спросил Laurynas Lazauskas 5 32014vEurope/Moscow11bEurope/MoscowWed, 05 Nov 2014 23:55:38 +0300 2014, 23:55:38

3 ответа


0

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 просто назначает 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 запрещены, так как их разрешение принесет больше вреда, чем пользы. Поскольку значения в любом случае будут перемещены (и с гарантированной копией 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, в отличие от указателей, не имеют нулевого состояния. Их нужно инициализировать с помощью либо ссылки, либо другого reference_wrapper .

    std::reference_wrapper<int> r; // Invalid
    
  • Сходство заключается в семантике мелкого копирования: указатели и reference_wrapper могут бытьпереназначен.

ответил Columbo 6 42014vEurope/Moscow11bEurope/MoscowThu, 06 Nov 2014 00:11:06 +0300 2014, 00:11:06
0

Другое отличие с точки зрения самодокументируемого кода состоит в том, что использование 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
ответил Edward Loper 31 MarpmTue, 31 Mar 2015 23:09:17 +03002015-03-31T23:09:17+03:0011 2015, 23:09:17
0

Вы можете думать об этом как об удобной обертке вокруг ссылок, чтобы вы могли использовать их в контейнерах.

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
ответил Barry 6 42014vEurope/Moscow11bEurope/MoscowThu, 06 Nov 2014 00:02:47 +0300 2014, 00:02:47

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132