C++11中std::reference_wrapper()和std::ref()

1、std::reference_wrapper

std::reference_wrapper is a new feature introduced by C++11, defined in <functional>头文件中

template< class T >
class reference_wrapper;

reference_wrapper wraps a reference into a copyable and allocatable object, which is a wrapper for the reference. It is usually used as a mechanism to store the reference in a standard container (such as std::vector), because the standard container is usually Could not store referenced.

such as:

  • In the container is the std::reference_wrapper object
    std::vector<std::reference_wrapper<ParticipantObserver>> observers_;

Reference_wrapper is a wrapper of copyable construction and assignable construction. It can wrap a reference into an object or wrap a reference into a function of template parameter type T;
the instance object of std::reference_wrapper can be saved and stored in a standard container , But it will be implicitly converted to T&, so std::reference_wrapper can be used as an argument of a function whose wrapped type is a parameter.

For example, in the following code, the parameter type of the function func is int, and the parameter passed to func is indeed an object of type std::reference_wrapper. This feature is the key to ensuring that the reference_wrapper object can be used as a function argument.

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

If the reference wrapped by reference_wrapper is callable, the reference_wrapper object is also callable;
 std::ref and std::cref are usually used to generate a reference_wrapper object;
reference_wrapper is often passed by reference to the std::bind function or std: :thread constructor.
Possible implementation of 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>;
  • In summary, the following three situations may need to be used:
  • Can not directly store references in vector, and do not want to make copies
  • The parameters thrown into lamba or template functions are references,
  • If the design intent is that the object will be modified after execution

You can also use the following code to define the reference_wrapper object:

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

Through the get function (r.get()) of the r object, the packaged element can be obtained.

The semantics of reference_wrapper and move are closely connected, which can save the overhead of copy construction of rvalue objects.

Std::reference_wrapper usage examples

#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 << ' ';
    }
}

Possible output:

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

Reference array creation

    It can also be used to create an array of references, for example:

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

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

    std::reference_wrapper is widely used in generic code. It stores pointers to objects and has all the functions of references. It also implements reference copying (including copy construction and copy assignment), and can store references in the program instead of the entire Object.
    How to choose reference_wrapper and shared_ptr? Both can implement pointer-level copy and operation, but the former does not allow default constructors, and methods such as resize cannot be used in the container. There may be some differences, but basically there is not much difference.

2、std::ref

std::ref() is a template function, and its function prototype is:

//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() is used to build a reference_wrapper

Construct a reference_wrapper type object, which has a reference to the passed elem variable. If the parameter itself is a reference_wrapper type x, a copy of x is created.

parameter:

elem:An lvalue reference, whose reference is stored in the object.

x:A reference_wrapper object, which is copied.

return value:

A reference_wrapper object with a T type element

Example:

// 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;
}

Output:

11

std::cref()

Construct a reference_wrapper constant type.

3. Why does c++11 introduce std::ref(), the difference between std::ref() and reference

       Std::ref mainly considers that when using functional programming (such as std::bind), it directly copies the parameters and cannot pass in references, so std::ref() is introduced. Use std::ref to pass in a reference when passing parameters in a template.

ref can use the wrapper type reference_wrapper to replace the originally recognized value type, and reference_wrapper can be implicitly converted to the reference type of the referenced value.

This problem also occurs not only when using bind, but also when programming with thread. When the thread method passes references, the outer layer must use ref to pass by reference, otherwise it is a shallow copy.

The std::bind() function needs to pass in a reference

#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;
}

Output:

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

Here we can find that when using bind, if ref is not used, the calling function is not referenced.

std::thread() requires a reference

Check the source code of thread, its constructor depends on a variaic template parameter list of rvalue-reference type:

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)...));
}

The parameters of the thread function are moved or copied by value. If the reference parameter needs to be passed to the thread function, it must be wrapped (for example using std::ref or 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;
}

In the std::promise example, std::ref is used to pass the future object to the task function of the reference parameter type.

    std::promise example  

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();

   If pr is directly passed in, a compilation error will occur:

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

    Indicates that the parameter types of the function call do not match.

The difference between std::ref() and reference

std::ref is just an attempt to simulate reference passing, and cannot really become a reference. In the case of non-templates, std::ref cannot achieve reference passing at all. Only when the template automatically deduces the type, ref can use the wrapper type reference_wrapper to replace the original The value type will be recognized, and reference_wrapper can be implicitly converted to the reference type of the value being quoted.

reference:

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

 

Guess you like

Origin blog.csdn.net/sunlin972913894/article/details/107130322