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

8739 ワード

1、std::reference_wrapper
std::reference_wrapperはC++11に導入された新しい特性であり、 に定義されている.
template< class T >
class reference_wrapper;

reference_wrapperは、標準コンテナでは通常参照を格納できないため、参照をコピー可能、割り当て可能なオブジェクトにパッケージします.
例:
  • 容器にはstd::reference_wrapperオブジェクト
  •     std::vector<:reference_wrapper>> observers_;

    reference_wrapperはコピー可能な構造と付与可能な構造の包装器であり、1つの参照をオブジェクトに包装したり、1つの参照をテンプレートパラメータタイプTの関数に包装したりすることができる.std::reference_wrapperのインスタンスオブジェクトは標準コンテナに保存および格納できますが、T&に暗黙的に変換されるためstd::reference_wrapperは、そのラップタイプをパラメータとする関数の実パラメータとして使用することができる.
    次のコードのように、関数funcのパラメータタイプはintであり、funcに渡されるパラメータはstd::reference_wrapperタイプのオブジェクト.この特性はreferenceを保証することです.wrapperオブジェクトは、関数の実パラメータのキーとして使用できます.
    void func(int param){
    std::cout << param << std::endl;
    }
    int a = 3;
    std::reference_wrapper ra = a;
    func(ra);

    reference_wrapperパッケージの参照は呼び出すことができます.reference_wrapperオブジェクトも呼び出すことができます.std::refとstd::crefは通常referenceを生成するために使用されます.wrapperオブジェクト;reference_wrapperは、std::bind関数またはstd::thread構造関数に参照によってオブジェクトを渡すことが多い.std::reference_wrapperの可能な実装
    namespace detail {
    template  constexpr T& FUN(T& t) noexcept { return t; }
    template  void FUN(T&&) = delete;
    }
     
    template 
    class reference_wrapper {
    public:
      // types
      typedef T type;
     
      // construct/copy/destroy
      template (std::declval()),
        std::enable_if_t>>()
      )>
      constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN(std::forward(u))))
        : _ptr(std::addressof(detail::FUN(std::forward(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
        operator() ( ArgTypes&&... args ) const {
        return std::invoke(get(), std::forward(args)...);
      }
     
    private:
      T* _ptr;
    };
     
    // deduction guides
    template
    reference_wrapper(T&) -> reference_wrapper;
  • をまとめると、
  • という3つのケースが必要になる可能性があります.
  • vectorには参照を直接格納できないし、コピーもしたくない
  • lambaまたはテンプレート関数を失うパラメータは参照であり、
  • 設計意図が実行完了すると被オブジェクトは
  • 修正される.
    次のコードを使用してreferenceを定義することもできます.wrapperオブジェクト:
    reference_wrapper r=x;// or auto r = ref(x);

    rオブジェクトのget関数(r.get())により、パッケージの要素を取得できます.
    reference_wrapperとmoveの意味は密接に接続されており、右値オブジェクトのレプリケーション構造のオーバーヘッドを節約できます.
    std::reference_wrapper使用例
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
     
    int main()
    {
        std::list l(10);
     
        std::iota(l.begin(), l.end(), -4);
        std::vector<:reference_wrapper>> 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 << "
    Contents of the list, as seen through a shuffled vector: "; for (int i : v){ std::cout << i << ' '; } std::cout << "

    Doubling the values in the initial list...

    "; 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

    参照配列の作成
    参照配列を作成することもできます.たとえば、次のようにします.
    int x=5,y=7,z=8;
    
    std::reference_wrapper arr[]{x,y,z};

        std::reference_wrapperは汎用コードで広く使われており、オブジェクトのポインタが格納されており、参照されるすべての機能があります.リファレンスのコピーも実現(コピー構造とコピー付与を含む)では、オブジェクト全体ではなくプログラムに参照を格納できます.reference_wrapperとshared_ptrはどのように選択しますか?どちらもポインタレベルのコピーと操作を実現できますが、前者はデフォルトの構造関数を許可せず、コンテナでもresizeなどの方法を使用できません.また、いくつかの違いがあるかもしれませんが、基本的にはありません大きな違いがあります.
    2、std::ref
    std::ref()はテンプレート関数で、その関数のプロトタイプは:
    //reference (1)	
    
    template  reference_wrapper ref (T& elem) noexcept;
    
    //copy (2)	
    
    template  reference_wrapper ref (reference_wrapper& x) noexcept;
    
    //move (3)     prvalue x                  ,  const T&&      prvalue x    。
    template  void ref (const T&&) = delete;

    std::ref()はreferenceを構築するために使用されます.wrapper
    referenceを構築するwrapperタイプオブジェクト.受信したelem変数の参照を持つオブジェクト.パラメータ自体がreferenceである場合wrapperタイプxの場合、xのコピーが作成されます.
    パラメータ:
    elem:An lvalue reference, whose reference is stored in the object.
    x:A reference_wrapper object, which is copied.
    戻り値:
    Tタイプの要素を持つreference_wrapperオブジェクト
    例:
    // ref example
    #include      // std::cout
    #include    // std::ref
    
    int main () {
      int foo (10);
    
      auto bar = std::ref(foo);
    
      ++bar;
    
      std::cout << foo << '
    '; return 0; }

    Output:
    11

    std::cref()
    referenceを作成するwrapper定数タイプ.
    3、c++11はなぜstd::ref()、std::ref()と参照の違いを導入するのか
    std::refは主に関数式プログラミング(例えばstd::bind)が使用される場合、パラメータを直接コピーし、参照を入力できないため、std::ref()を導入する.std::refを使用すると、テンプレートの参照時に参照を入力できます.
    refはパッケージタイプreferenceで使用できます.wrapperは、本来認識される値タイプの代わりにreference_wrapperは、参照される値の参照タイプに暗黙的に変換することができる.
    bindを使用する場合だけでなく、threadを使用してプログラミングを行う場合でも、threadの方法でリファレンスを渡す場合は、外層をrefでリファレンスを渡す必要があります.そうしないと、浅いコピーになります.
    std::bind()関数は参照を入力する必要があります
    #include 
    #include 
    
    //std::ref          ( std::bind)    ,        ,     
    void f(int &a,int &b,int &c)
    {
        std::cout< f1 = bind(f,n1,n2,ref(n3));
     
        f1();
        std::cout<

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

    ここでbindを用いる場合,refを用いない場合,呼び出し関数は参照されないことが分かった.
    std::thread()入力リファレンスが必要
    構造関数がrvalue-referenceタイプのvariaic templatesパラメータリストに依存するthreadのソースコードを表示します.
    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)...));
    }

    スレッド関数のパラメータは、値によって移動またはコピーされます.参照パラメータがスレッド関数に渡される必要がある場合は、std::refまたはstd::crefを使用するなど、パッケージ化する必要があります.
    #include 
    #include 
    #include 
    
    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 <

    std::promiseの例では、std::refを使用してfutureオブジェクトを参照パラメータタイプのタスク関数に渡します.
    std::promise例
    std::promis pr;
    std::thread t([](std::promise& p) {p.set_value_at_thread_exit(9);},std::ref(pr));
    std::future f = pr.get_future();
    auto r = f.get();

    prに直接転送すると、コンパイルエラーが発生します.
    error C2661: “std::tuple,std::promise>::tuple”:          2    

    関数呼び出しのパラメータタイプが一致しないことを示します.
    std::ref()と参照の違い
    std::refはリファレンス伝達をシミュレートしようとするだけで、本当にリファレンスになることはできません.テンプレート以外の場合、std::refはリファレンス伝達を実現することはできません.テンプレート自動導出タイプのみの場合、refはパッケージタイプreference_wrapperは、本来認識される値タイプの代わりにreference_wrapperは、参照される値の参照タイプに暗黙的に変換することができる.
    参照先:
    https://blog.csdn.net/commshare/article/details/107133634