C ++ 11 中 estándar :: envoltorio_de_referencia () 和 estándar :: ref ()

1 、std :: reference_wrapper

std :: reference_wrapper es una nueva característica introducida por C ++ 11, definida en <functional>头文件中

template< class T >
class reference_wrapper;

reference_wrapper envuelve una referencia en un objeto copiable y asignable, que es un contenedor para la referencia. Por lo general, se usa como un mecanismo para almacenar la referencia en un contenedor estándar (como std :: vector), porque el contenedor estándar generalmente es Could no se hace referencia a la tienda.

como:

  • En el contenedor está el objeto std :: reference_wrapper
    std::vector<std::reference_wrapper<ParticipantObserver>> observers_;

Reference_wrapper es un contenedor de construcción copiable y asignable. Puede empaquetar una referencia en un objeto o empaquetar una referencia en una función del parámetro de plantilla tipo T;
el objeto de instancia de std :: reference_wrapper se puede guardar y almacenar en un contenedor estándar, Pero se convertirá implícitamente a T &, por lo que std :: reference_wrapper se puede usar como argumento de una función cuyo tipo envuelto es un parámetro.

Por ejemplo, en el siguiente código, el tipo de parámetro de la función func es int, y el parámetro pasado a func es de hecho un objeto de tipo std :: reference_wrapper. Esta característica es la clave para asegurar que el objeto reference_wrapper pueda usarse como un argumento de función.

void func(int param){
std::cout << param << std::endl;
}
int a = 3;
std::reference_wrapper<int> ra = a;
func(ra);

Si la referencia envuelta por reference_wrapper es invocable, el objeto reference_wrapper también es invocable;
 std :: ref y std :: cref generalmente se usan para generar un objeto
reference_wrapper ; reference_wrapper a menudo se pasa por referencia a la función std :: bind o std: : constructor de hilo.
Posible implementación de std :: reference_wrapper

namespace detail {
template <class T> constexpr T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
 
template <class T>
class reference_wrapper {
public:
  // types
  typedef T type;
 
  // construct/copy/destroy
  template <class U, class = decltype(
    detail::FUN<T>(std::declval<U>()),
    std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>()
  )>
  constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
    : _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
  reference_wrapper(const reference_wrapper&) noexcept = default;
 
  // assignment
  reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
 
  // access
  constexpr operator T& () const noexcept { return *_ptr; }
  constexpr T& get() const noexcept { return *_ptr; }
 
  template< class... ArgTypes >
  constexpr std::invoke_result_t<T&, ArgTypes...>
    operator() ( ArgTypes&&... args ) const {
    return std::invoke(get(), std::forward<ArgTypes>(args)...);
  }
 
private:
  T* _ptr;
};
 
// deduction guides
template<class T>
reference_wrapper(T&) -> reference_wrapper<T>;
  • En resumen, es posible que deban utilizarse las siguientes tres situaciones:
  • No puede almacenar referencias directamente en vector y no desea hacer copias
  • Los parámetros lanzados a las funciones de plantilla o lamba son referencias,
  • Si la intención del diseño es que el objeto se modifique después de la ejecución

También puede utilizar el siguiente código para definir el objeto reference_wrapper:

reference_wrapper r=x;// or auto r = ref(x);

A través de la función get (r.get ()) del objeto r, se puede obtener el elemento empaquetado.

La semántica de reference_wrapper y move están estrechamente relacionadas, lo que puede ahorrar la sobrecarga de la construcción de copias de los objetos rvalue.

Ejemplos de uso de std :: reference_wrapper

#include <algorithm>
#include <list>
#include <vector>
#include <iostream>
#include <numeric>
#include <random>
#include <functional>
 
int main()
{
    std::list<int> l(10);
 
    std::iota(l.begin(), l.end(), -4);
    std::vector<std::reference_wrapper<int>> v(l.begin(), l.end());
 
    // can't use shuffle on a list (requires random access), but can use it on a vector
    std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});
 
    std::cout << "Contents of the list: ";
    for (int n : l){ 
        std::cout << n << ' ';
    }
 
    std::cout << "\nContents of the list, as seen through a shuffled vector: ";
    for (int i : v){
        std::cout << i << ' ';
    }
 
    std::cout << "\n\nDoubling the values in the initial list...\n\n";
    for (int& i : l) {
        i *= 2;
    }
 
    std::cout << "Contents of the list, as seen through a shuffled vector: ";
    for (int i : v){
       std::cout << i << ' ';
    }
}

Salida posible:

Contents of the list: -4 -3 -2 -1 0 1 2 3 4 5 
Contents of the list, as seen through a shuffled vector: 3 -3 -2 2 0 4 5 -4 -1 1 

Doubling the values in the initial list...

Contents of the list, as seen through a shuffled vector: 6 -6 -4 4 0 8 10 -8 -2 2

Creación de matriz de referencia

    También se puede utilizar para crear una matriz de referencias, por ejemplo:

int x=5,y=7,z=8;

std::reference_wrapper<int> arr[]{x,y,z};

    std :: reference_wrapper se usa ampliamente en código genérico. Almacena punteros a objetos y tiene todas las funciones de referencias. También implementa la copia de referencias (incluida la construcción de copias y la asignación de copias), y puede almacenar referencias en el programa en lugar de todo el Objeto .
    ¿Cómo elegir reference_wrapper y shared_ptr? Ambos pueden implementar copia y operación a nivel de puntero, pero el primero no permite constructores predeterminados, y métodos como resize no se pueden usar en el contenedor. Puede haber algunas diferencias, pero básicamente no hay mucha diferencia.

2 、 estándar :: ref

std :: ref () es una función de plantilla, y su prototipo de función es:

//reference (1)	

template <class T> reference_wrapper<T> ref (T& elem) noexcept;

//copy (2)	

template <class T> reference_wrapper<T> ref (reference_wrapper<T>& x) noexcept;

//move (3) 明确禁止prvalue和x值值类别类型的对象从与所述功能被使用,并且const T&&不结合到所有prvalue和x值的对象。
template <class T> void ref (const T&&) = delete;

std :: ref () se usa para construir un reference_wrapper

Construya un objeto de tipo reference_wrapper, que tenga una referencia a la variable elem pasada. Si el parámetro en sí es un reference_wrapper tipo x, se crea una copia de x.

parámetro:

elem : Una referencia de valor , cuya referencia se almacena en el objeto.

x: un objeto reference_wrapper , que se copia.

valor de retorno:

Un objeto reference_wrapper con un elemento de tipo T

Ejemplo:

// ref example
#include <iostream>     // std::cout
#include <functional>   // std::ref

int main () {
  int foo (10);

  auto bar = std::ref(foo);

  ++bar;

  std::cout << foo << '\n';

  return 0;
}

Salida:

11

std :: cref ()

Construya un tipo de constante reference_wrapper.

3. ¿Por qué c ++ 11 introduce std :: ref (), la diferencia entre std :: ref () y reference

       std :: ref considera principalmente que cuando se usa programación funcional (como std :: bind), copia los parámetros directamente y no puede pasar referencias, por lo que se introduce std :: ref (). Utilice std :: ref para pasar una referencia al pasar parámetros en una plantilla.

ref puede usar el tipo de contenedor reference_wrapper para reemplazar el tipo de valor originalmente reconocido, y reference_wrapper se puede convertir implícitamente al tipo de referencia del valor referenciado.

Este problema también ocurre no solo cuando se usa bind, sino también cuando se programa con thread.Cuando el método thread pasa referencias, la capa externa debe usar ref para pasar por referencia, de lo contrario es una copia superficial.

La función std :: bind () necesita pasar una referencia

#include <functional>
#include <iostream>

//std::ref主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用
void f(int &a,int &b,int &c)
{
    std::cout<<"in function a = "<<a<<"  b = "<<b<<"  c = "<<c<<std::endl;
    a += 1;
    b += 10;
    c += 100;
}
 
int main(){
 
    int n1 = 1 ,n2 = 10,n3 = 100;
 
    function<void()> f1 = bind(f,n1,n2,ref(n3));
 
    f1();
    std::cout<<"out function a = "<<n1<<"  b = "<<n2<<"  c = "<<n3<<std::endl;
    f1();
    std::cout<<"out function a = "<<n1<<"  b = "<<n2<<"  c = "<<n3<<std::endl;
 
    return 0;
}

Salida:

in function a = 1  b = 10  c = 100
out function a = 1  b = 10  c = 200
in function a = 2  b = 20  c = 200
out function a = 1  b = 10  c = 300

Aquí podemos encontrar que cuando se usa bind, si no se usa ref, no se hace referencia a la función de llamada.

std :: thread () necesita pasar una referencia

Verifique el código fuente del hilo, su constructor depende de una lista de parámetros de plantilla variable del tipo rvalue-reference:

template::type, thread>::value>::type>explicit
thread(_Fn&& _Fx, _Args&&... _Ax){
    // construct with _Fx(_Ax...)
    _Launch(&_Thr,
        _STD make_unique, decay_t<_Args>...> >(
            _STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...));
}

Los parámetros de la función de subproceso se mueven o se copian por valor. Si el parámetro de referencia debe pasarse a la función de subproceso, debe ajustarse (por ejemplo, usando std :: ref o std :: cref).

#include <functional>
#include <iostream>
#include <thread>

void method(int & a){ a += 5;}

using namespace std;
int main(){

    int a = 0;
    // each reference used by the threads would refer to the same object.
    thread th(method,ref(a));
    th.join();
    cout << a <<endl;

    /*thread th2(method, a);  //浅拷贝
    th2.join();
    cout << a <<endl;*/
    return 0;
}

En el ejemplo std :: promise, std :: ref se usa para pasar el objeto futuro a la función de tarea del tipo de parámetro de referencia.

    std :: ejemplo de promesa  

std::promis<int> pr;
std::thread t([](std::promise<int>& p) {p.set_value_at_thread_exit(9);},std::ref(pr));
std::future<int> f = pr.get_future();
auto r = f.get();

   Si pr se pasa directamente, se producirá un error de compilación:

error C2661: “std::tuple,std::promise>::tuple”: 没有重载函数接受 2 个参数

    Indica que los tipos de parámetros de la llamada a la función no coinciden.

La diferencia entre std :: ref () y reference

std :: ref solo está tratando de simular el paso de referencia, y realmente no puede convertirse en una referencia. En el caso de las no plantillas, std :: ref no puede lograr el paso de referencia en absoluto. Cuando solo la plantilla deduce automáticamente el tipo, ref puede usar el tipo de envoltura reference_wrapper para reemplazar el original. El tipo de valor será reconocido, y reference_wrapper se puede convertir implícitamente al tipo de referencia del valor que se cotiza.

referencia:

https://blog.csdn.net/commshare/article/details/107133634

 

Supongo que te gusta

Origin blog.csdn.net/sunlin972913894/article/details/107130322
Recomendado
Clasificación