std::moveとstd::forward

4550 ワード

前に左引用と右引用について書きましたが、Move ConstructorとMove Assignmentについて話しました.このセクションでは最後のstd::moveとstd::forwardについて話します.では、この小さなシリーズは終わります.
    std::move
Move ConstructorとMove Assignmentの使い方を知っていますが、Rvalue ReferenceをLvalueにバインドする方法はまだありません.この時はstd::moveが出場する必要があります.std::moveのプロトタイプ:
template 
typename remove_reference::type&& move(T&& t){
    return static_cast::type&&> (t);
}

このコードは短いですが、簡単ではありません.まずT&&、これはテンプレートパラメータタイプのRvalue Referenceです.単純なRvalue Referenceと区別してください.テンプレートパラメータタイプのRvalue Referenceには次の式があります.
ここでTは具体的なタイプで、TはLvalueまたはRvalueであってもよいし、Lvalue ReferenceまたはRvalue Referenceであってもよい.より便利に話すために、Tの引用原型をTTとする.
T==TTの場合、T deduced TT&
T==TT&の場合、T&==TT&&==TT&
T==TT&&の場合、T&&&=TT&&&&==TT&&
ここでT&も併せて言いますが、これはテンプレートパラメータタイプのLvalue Referenceです.
T==TTの場合、T deduced TT&
T==TT&の場合、T&==TT&==TT&
T==TT&&の場合、T&==TT&&==TT&
上の式があれば、理解するのはずっと簡単です.入力T&&については、上記の式に従って任意のタイプを受け入れることができます.出力typename remove_reference::type&&==TT&&&なので、元のタイプのRvalue Referenceを返します.例を見てみましょう
string s1("Hello"), s2;
s2 = std::move(string("world"));       //moving from a Rvalue,   string&& std::move(string &&t)
s2 = std::move(s1);                    //moving from a Lvalue,    string&& std::move(string &t)

ここではstaticについてお話ししますcast,static_castは強力で、従来の機能だけでなく、参照のタイプを明示的に変えることができます.Lvalue ReferenceをRvalue Referenceに変換する場合はstatic_を使用することもできます.castは変換しますが、std::moveを使うと明らかに便利です.
    std::forward
まず例を見てみましょう.
template 
void flip1(F f, T1 t1, T2 t2){
    f(t2, t1);
}

このテンプレート関数は、1つの関数形式(2つのパラメータを受け入れる)と2つのパラメータを定義していることがわかります.この関数には大きな問題はないように見えますが、実際には大きな問題があります.
void f (int v1, int &v2){
    cout << v1 << " " << ++v2 << endl;
}

f(42, i);            //f     i   
flip1(f, j, 42)      //  flip1   f,    f      j  

fはjの値を変えることができなくなった.これは大きな問題であり、最初の設計の初心に合わない.テンプレートを復元します.
void flip1(void(*f)(int, int&), int t1, int t2);

jの値は、値によってt 1に渡され、実際にはcopy動作であり、fの参照パラメータはjではなくt 1にバインドされる.
したがって、flip 1関数については、fでパラメータの値を変更できることを保証し、このようなテンプレート関数に対しても、パラメータがconstであるかどうかを保証する目標を達成する必要があります.ここでは、前述の「テンプレートパラメータタイプのRvalue Reference」を使用します.T 1&&とT 2&&を使用すると、flip 1のパラメータのプロパティ(constを含む)を保持できます.
template 
void flip2 (F f, T1 &&t1, T2 &&t2){
    f(t2, t1);
}

前のRvalue Reference式により、
void flip1(void(*f)(int, int&), int& t1, int& t2);        //int deduced to int&

これによりfにおいてjの値を変更することができる.
次はもう一つの関数を見てみましょう
void g(int &&i, int& j){
    cout << i << j << endl;
}

flip2(g, i, 42);           //      LValue Reference       RValue Reference 

このうち42はRValueであるが、t 2がバインドされている場合、t 2自体はLRealue Referenceである.このときt 2がgに再び伝達されると問題がある.LRealue ReferenceをRValue Referenceの初期化に使用できないからである.
上記の2つの例に基づいて、入力パラメータを元のプロパティに復元することが重要であることがわかります.そうしないと、問題が発生します.このときstd::forwad関数を用いた.まずstd::forwardのプロトタイプを見てみましょう.
template 
T&& forward(typename std::remove_reference::type& param) //      
{
    return static_cast(param);
}

template 
T&& forward(typename std::remove_reference::type&& param)  //      
{
    //param       ,T        ,  T           。
    static_assert(!std::is_lvalue_reference<_tp>::value, "template argument"  
    " substituting _Tp is an lvalue reference type"); 
    
    return static_cast(param);
}

この中にはstd::remove_reference::type&std::remove_reference::type&&&、彼らはT&&&と区別があり、彼らは真のタイプのLValue ReferenceとRValue Referenceなので、パラメータの真のタイプに応じて対応するforward関数に一致します.static_cast,この中のT&&はまた元のパラメータタイプを完全に復元することができる.
したがって、flip関数は、以上のように書くべきです.
template 
void flip(F f, T1 &&t1, T2 &&t2){
    f(std::forward(t2), std::forward(t1));
}

以上std::moveとstd::forwardを分析しました.