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



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

1054   4  

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>:

  1. он должен давать ссылочную семантику объектам, переданным в качестве параметра значения в шаблоны функций. Например, у вас может быть большой объект функции, который вы хотите передать в std::for_each() который принимает свой параметр объекта функции по значению. Чтобы избежать копирования объекта, можно использовать

    std::for_each(begin, end, std::ref(fun));
    

    передача аргументов как std::reference_wrapper<T> до std::bind() выражение довольно часто связывает аргументы с помощью ссылка, а не по значению.

  2. при использовании 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

    Ничего не найдено.