C++面接左値、右値、左値参照、右値参照


1、左および右
左値(left-values)、略称:lvalues、located valueは値を位置決めすることができ、その格納アドレスの値を明確にすることができ、より正確にはアドレスベースの使用を意味する.
右(right-values)、略称:rvalues、read value読み取り可能な値.通常は、代入演算=右側の定数値、フォント値、または関数の戻り値を指し、具体的な代入名がなく、アドレスアクセスができず、代入式が終了すると破棄されます.
 
一般的には、左の値は変数のアドレスに対応し、右の値は変数の値に対応し、まず左の値と右の値について言えば、彼らは決して簡単な等号の左と右の違いではなく、まとめて言えば:
  • 左の値はアドレッシングできますが、右の値はできません.
  • 左の値は割り当てられ、右の値は割り当てられず、左の値に割り当てることができます.
  • 左の値は可変、右の値は可変ではありません(ベース・タイプにのみ適用され、ユーザー定義タイプの右の値参照はメンバー関数で変更できます).
  • int value=fun();

    最後に、c++11には、c++11に追加された右の値参照に関連する式であり、このような式は通常、移動されるオブジェクトである.
    2、左参照と右参照
    左値参照は左値への参照タイプで、T&aで表されます
    右値参照は右値への参照タイプで、T&&aで表されます
    左参照と右参照は同じ参照であり、宣言と同時に初期化する必要があります(文法規定を参照)
    アドレスと参照を混同しないでください.&説明子の前にタイプ宣言が付いている場合は、参照です.そうでない場合は、アドレス(宣言および初期化でなければなりません)を取ります.一般的には、「=」号の左側に参照があり、右側にアドレスを取ります. 
     
    左値参照:通常説明されている参照で、参照は元の左値の別名であり、定義時に初期化が完了し、変更できません.左参照と左参照は、メモリ内の同じコンテンツデータを共通に使用します.
    const int&a = 1 //              
    const int a = 1 //                     ,     

    関数は、パラメータを値伝達方式で伝達し、関数値を返す(たとえば、戻りタイプが複雑なクラスタイプ(class))と、コピー構造が実行され、多くの恐れのないコピー構造のオーバーヘッドをもたらします.
    これは,値の代わりにconst参照をできるだけ関数パラメータとして伝達する理由であり,ある程度効率を向上させることができる.
     
    右値参照:つまり(Move Semantics:移動の意味)、無意味なコピー付与操作を避けるために、C++11は右値参照の概念を提出し、その本質は右値の所有権を引き継ぐ(元のメモリコンテンツを破棄するのではなく、所有権を交付されたオブジェクトに渡す)ことである.
    int &&value = 1    ;//         
    int &&value = fun();//              
  • は、左の値に対して右の値のライフサイクルが短く、関数の一時的な戻り値のように、制御権を安全に移行することができます.
  • は、右値のリソースを解放するのではなく、右値参照で使用し続け、大量のコピー、レプリケーションによるオーバーヘッドを削減します.
  • コンパイラはデフォルトで戻り値最適化をオンにし、重複オブジェクト構造の問題を解決し、右値参照を使用すると言語レベルで実現できます.

  • C++11が導入した右値参照は深い性能向上をもたらし、従来のレプリケーション、コピーの煩雑な操作を変え、よりスマートなモバイル技術を採用し、その本質は所有権を移転することによって、メモリの内容を変えずに資源の移転を完了する.
    例えば、ソースコードSTLでは、vectorの拡張は、右の値参照を使用して効率を向上させる.
    Vectorのsizeがcapacityの一定の割合に大きくなると、元のデータを新しい空間に移行しながら、より大きなメモリ領域を申請する必要があります.モバイル・テクノロジーの前に、これは大量の要素の深いコピーを意味し、モバイル・テクノロジーを採用するには、元の空間を解放する必要はなく、元の空間の所有権を新しい空間作成者に渡すだけでよい.
    追加の知識点の深いコピー、浅いコピー:https://blog.csdn.net/u014430031/article/details/115383480
    生面の2つのセグメントをまとめると、左値参照と右値参照シート関数パラメータは、オブジェクトのコピーと構造を回避できます.
    しかし、右の値の参照によって右の値を変更することは意味がありません.左の値の参照によって左の値を変更することは意味があります.
    3、Perfect Forwarding:完全転送(move、forward関数)
    実際std::moveは、左の値を右の値に変換するタイプの変換器です.その実現を見てみましょう.
    template 
    typename remove_reference::type&& move(T&& t)
    {
        return static_case::type&&>(t);
    }

    共通参照
    まずmoveの入力パラメータを見てみましょう.moveの入力パラメータタイプを汎用参照タイプと呼びます.共通参照とは?つまり、左の値も右の値も受信できます.
    コードにはautoとテンプレートで定義されたT&&&の2種類の共通参照があります.実際autoはテンプレートのTであり,それらは等価である.
    テンプレートのタイプ導出
    通用引用は強いですね.左の値も右の値も受信できますが、どうすればいいのでしょうか.これでテンプレートのタイプの導出についてお話しします.
    コードには、基本タイプと参照タイプの2つのタイプがあります.
    ≪基本タイプ|Basic Type|emdw≫:アドレスを持たない通常の元のタイプ.
    ≪参照タイプ|Reference Type|emdw≫:格納されている値はアドレスで、データが格納されている場所を指します.
    テンプレートのタイプの導出ルールは複雑ですが、ここでは簡単に説明します.興味のある学生はC++11の規範を調べることができます.具体的な例を挙げましょう.
    template 
    void f(T param);  //   
    
    
    //        ,             ,             
    int x = 10;         // x int
    const int& rx = x;  // rx const int &
    f(rx);              // T int

    第1のクラスについて、その導出の原則は、関数パラメータの伝達値が元の値に影響しないため、実際に伝達されたパラメータが通常の変数、定数、参照であっても、最終的には修飾されていない元のタイプに劣化します.
     
    template 
    void func(T& param); //   
    
    template 
    void function(T&& param); //   
    
    
    //        ,            ,     ;      ,         T   
    int x = 10;         // x int
    const int& rx = x;  // rx const int &
    func(x);            // T int
    func(rx);           // T const int
    
    
    //         ,         
    function(x);        // T int&
    function(5);        // T int

    2番目のクラスは、テンプレートタイプが参照(左参照と右参照を含む)またはポインタテンプレートです.このようなタイプの導出の原則は、対等な数の参照記号を除去し、他のキーワードのようにすることである.また、上記の例では、func(x)のxのタイプはint&であり、T&と一緒に置くとTがintであることがわかります.別の例function(x)であり、xがint&であり、T&&とともにTがint&であることが分かる.
    戻りタイプ
    moveのremoveを見てみましょうreferenceは見慣れないので、次にremoveを分析します.reference類、また何の役に立つか見てみましょう.実は、その名前で大体推測できるはずですが、テンプレートで参照を除去します.その実現を見てみましょう.
    template 
    struct remove_reference{
        typedef T type;  //  T      type
    };
    
    template 
    struct remove_reference //    
    {
        typedef T type;
    }
    
    template 
    struct remove_reference //    
    {
       typedef T type;
    }

    上のコードからremove_reference処理後,Tの参照は取り除かれた.先にmoveのタイプによってTがint&&&であることを自動的に導出したと仮定すると,remove_は再びテンプレートによって導出される.referenceのtypeメンバーは、typeのタイプがintであることがわかります.
    remove_referenceはテンプレートの自動導出を用いて実パラメトリック参照後のタイプを取得した.今、move関数を見に戻ったとき、一目で分かったのではないでしょうか.これまで理解できなかった5行のコードは、次のようになりました.
    int && move(int&& && t){
        return static_case(t);
    }
    
    // 
    int && move(int& && t){
        return static_case(t);
    }

    上で変換した後、このコードはずっとはっきりしていて、moveが実際に1つのタイプの強制変換をしたことがわかります.左参照の場合は、強制的に右参照に変換します.
    参照の折りたたみ
    上のコードは簡単に見えますが、パラメータint& &&int && &&は違和感があります.C++コンパイラはこの2つのタイプをサポートしていないからです.あれ!これはどういうことですか.
    リファレンス折りたたみルールを確認します.
     
     
    Expanded type
    Collapsed type T& & T& T& && T& T&& & T& T&& && T&&
    要約すると、左値参照は常に左値参照に折り畳まれ、右値参照は常に右値参照に折り畳まれます.
     
    forwardの役割
    std::forwardは完全転送と呼ばれ、元の 属性を一定に保つ役割を果たしています.どういう意味ですか.一般的には、元の値が左であればstd::forward処理後の値は左です.元の値が右の場合、std::forward処理後も右の値です.
    forward実装原理
    forward実装の原理を解析するには、まずforwardコード実装を見てみましょう.私たちは前にstd::moveを分析する基礎を持っていたので、forwardコードを見るとあまり難しくないはずです.
    ……
    
    template 
    T&& forward(typename std::remove_reference::type& param)
    {
        return static_cast(param);
    }
    
    template 
    T&& forward(typename std::remove_reference::type&& param)
    {
        return static_cast(param);
    }
    
    ……
    

    forwardは2つのテンプレート関数を実現し,1つは左値を受信し,もう1つは右値を受信する.上にコードがあります.
    typename std::remove_reference::type
    

    の意味std::moveを分析するときに詳しく説明しましたが、その意味は参照を削除するパラメータタイプを得ることです.したがって、上の2つの上テンプレート関数のうち、1つ目は左値参照テンプレート関数であり、2つ目は右値参照テンプレート関数である.
    続いてstd::forwardテンプレート関数は、入力されたパラメータを強制的にタイプ変換し、変換のターゲットタイプは参照折り畳みルールに合致するため、左パラメータは最終的に変換されても左値であり、右パラメータは最終的に右値に変換されます.
     
    まとめ:
    右の値の参照左の値と右の値を区別します.不要なメモリ割り当てとレプリケーション操作を排除することで、アプリケーションのパフォーマンスを向上させることができます.また、任意のパラメータを受け入れる関数のバージョンを記述し、別の関数を直接呼び出すように別の関数に転送することもできます.