MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の基礎 - 左辺値 と 左辺値参照のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の基礎 - 左辺値 と 左辺値参照
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == C言語では、代入演算子=の左側にあるものを左辺値(lvalue)、右側(rvalue)にあるものを右辺値として決められている。<br> C++では、左辺値は名前を持つオブジェクト(&演算子でアドレスを取得できる)、右辺値は名前を持たない一時オブジェクトのことである。<br> <br> ここでは、右辺値、左辺値、右辺値参照、左辺値参照、ムーブ、ユニバーサル参照、完全転送の意味を記載する。<br> <br><br> == 右辺値と左辺値 == メソッドの戻り値、式の結果等は右辺値である。<br> <syntaxhighlight lang="c++"> class X; int func(); int main() { X(); // クラスのコンストラクタは右辺値 func(); // func関数の戻り値は右辺値 int i = 1; // 変数iは左辺値, 1は右辺値 i + 1; // 式i + 1は右辺値 } </syntaxhighlight> <br> 右辺値は左辺値に変換できないため、以下はコンパイルエラーとなる。<br> <syntaxhighlight lang="c++"> int main() { int x; // 変数xは左辺値 (x + 1) = 9; // コンパイルエラー } </syntaxhighlight> <br> 左辺値とは、変数に代入されている値のことである。<br> <syntaxhighlight lang="c++"> class X; int main() { int i; // 変数iは左辺値 X x; // Xクラスのインスタンスは左辺値 X *ptr_X = &x; // ポインタ変数は左辺値 } </syntaxhighlight> <br> 以下に、右辺値と左辺値の比較を示す。<br> <syntaxhighlight lang="c++"> int x = 1; // xは左辺値、1は右辺値 int y = x + 1; // yは左辺値、式x + 1は右辺値、xは左辺値 int z = func(y + 2); // zは左辺値、func関数の戻り値は右辺値、式y + 2も右辺値 struct Point { int x = 0; int y = 0; }; Point pt = Point(); // Pointクラスのインスタンスptは左辺値、Pointクラスのコンストラクタは右辺値 1; // 1は右辺値 func(z); // func関数の戻り値は右辺値 // 戻り値はコピーも束縛もしていないため、以降使用できない </syntaxhighlight> <br><br> == 左辺値参照と右辺値参照 == 左辺値参照は、左辺値を束縛(※)すること、または、その参照変数のことである。<br> 左辺値参照は右辺値を参照することができないが、<u>constが付加された左辺値参照は右辺値を参照する</u>ことができる。<br> <br> <u>※束縛</u><br> <u>右辺値・左辺値に関わらず、参照を初期化することを束縛するという。</u><br> <u>参照 = 束縛対象といった代入の形、または、束縛対象を関数の実引数にする形で束縛される。</u><br> <u>参照を使用する時、束縛対象(参照元)の値を返す。</u><br> <br> <u>また、参照に参照を代入する時、代入元の束縛対象を代入先の束縛対象にする。</u><br> <syntaxhighlight lang="c++"> class X {}; void f(X &x) {} void g(const X &x) {} int main() { X x; f(x); // コンパイル可能 // コピーコンストラクタの引数は左辺値参照 g(x); // コンパイルエラー // Xクラスのインスタンスは左辺値のため、constが付加された引数には指定できない g(X()); // コンパイル可能 // constが付加された引数は右辺値を参照できる } </syntaxhighlight> <br> 右辺値参照は、右辺値を束縛(※)すること、または、その参照変数のことである。<br> 右辺値参照は、<データ型>&& <変数名>と記述する。<br> <br> 右辺値参照は型であるため、左辺値になることができる。<br> <syntaxhighlight lang="c++"> int func() { return 20; } int main() { int&& i = 10; // 右辺値10を右辺値参照変数iに束縛 int&& j = func(); // func関数の戻り値(右辺値)を右辺値参照変数jに束縛 std::cout << i << std::endl; // 10 std::cout << j << std::endl; // 20 i = j; // 右辺値参照は左辺値になることもできるため、コンパイル可能 std::cout << i << std::endl; // 20 std::cout << j << std::endl; // 20 } </syntaxhighlight> <br> 以下に、左辺値参照と右辺値参照の比較を示す。<br> <syntaxhighlight lang="c++"> // []は束縛対象(参照元), <>はコピーされた値 int x = 1; // xは左辺値<1> 1は右辺値 int& lref1 = x; // lrefは左辺値参照[x] xは左辺値 int& lref2 = lref; // lref2は左辺値参照[x] lref1は左辺値参照[x] // int& lref3 = 1; // コンパイルエラー // lref3は左辺値参照 1は右辺値 int y = lref1; // yは左辺値<xの値> lrefは左辺値参照[x] int&& rref1 = 1; // rref1が右辺値参照[1] 1は右辺値 // int&& rref2 = x; // コンパイルエラー // rref2が右辺値参照 xは左辺値 // int&& rref3 = lref1; // コンパイルエラー // rref3が右辺値参照 lrefは左辺値参照 // int&& rref4 = rref1; // コンパイエルエラー // rref4が右辺値参照 rrefは右辺値参照 int&& rrefm = std::move(lref1); // rrefmが右辺値参照 std::move(lref1)は右辺値 int w = rref1; // wが左辺値<1> rref1は右辺値参照[1] </syntaxhighlight> <br> 以下に、constを付加した左辺値参照も示す。<br> 一般的に、constを付加した右辺値参照は使用されない。(constを付加したオブジェクトの内部を変更すべきではないため)<br> <syntaxhighlight lang="c++"> // []は束縛対象(参照元), <>はコピーされた値 int x = 1 const int &clref1 = x; // clref1がconst左辺値参照[1] xは左辺値 const int &clref2 = lref1; // clref2がconst左辺値参照[1] lref1は左辺値参照[1] const int &clref3 = 2; // constが付加された左辺値参照は、右辺値を参照できる // clref3がconst左辺値参照[2] 2は右辺値 int z = clref1; // zが左辺値<1> clrefはconst左辺値参照[1] </syntaxhighlight> <br><br> == ムーブ == C++11以降、代入とコンストラクタにはコピーとムーブがある。<br> コピーは、データを全てコピーするため重い処理になるが、ムーブは、ポインタとサイズ情報のコピーのみを行うため軽い。<br> ただし、ムーブ元のオブジェクトのデータは、ムーブ後は不定になるため、そのオブジェクトは使用できなくなる。<br> <br> ムーブ先のオブジェクトがムーブに対応している場合、左辺値を<code>std::move</code>メソッドの引数に指定して、<br> 代入、または、コンストラクタの引数に指定するだけでムーブができる。<br> <br> 特に、右辺値参照はムーブで使用されることが多い。<br> <br> <u>※std::moveメソッド</u><br> <u><code>std::move</code>メソッドは、左辺値を右辺値にキャストする。</u><br> <u>右辺値なので、代入した場合は、<code>operartor=(const type&)</code>、<code>ムーブオペレータoperartor=(type&&)</code>が使用される。</u><br> <u>ムーブオペレータは、ポインタの挿げ替えとムーブ元オブジェクトを無効にする機能が実装されている。</u><br> <br> 例えば、aの値をbに代入する時、インスタンスのコピーが行われて、メモリ上に2つのインスタンスが存在することになる。<br> もし、aが指すインスタンスが以降で使用されない場合、aのポインタをbが持つだけでよいため、コピー操作は必要無い。<br> <syntaxhighlight lang="c++"> class Test; Test a = Test(); Test b = a; // aはこれ以降使用しない </syntaxhighlight> <br> ある変数が持つオブジェクトを別の変数に割り当てて、その変数からはオブジェクトを使わないようにする操作をムーブという。<br> ムーブは、ムーブコンストラクタやムーブ演算子を使用して表現する。<br> <br> ムーブコンストラクタやムーブ演算子の引数に、右辺値参照が現れる。<br> ムーブに対して、コピー演算子やコピーコンストラクタの引数は、左辺値参照となる。<br> <br> <code>std::move</code>メソッドは、左辺値を右辺値にキャストするものである。<br> <syntaxhighlight lang="c++"> #include <iostream> #include <utility> class Counter { private: int m_cnt; public: Counter() : m_cnt(0) { std::cout << "Default" << std::endl; } Counter(const Counter &c) { std::cout << "Copy" << std::endl; } Counter(Counter &&c) { m_cnt = c.getCnt(); std::cout << "Move" << std::endl; } ~Counter() {} int getCnt() {return m_cnt;} }; int main() { Counter c1, // 引数無しのコンストラクタ c2; // 引数無しのコンストラクタ Counter c3(c1); // コピーコンストラクタ Counter c4(std::move(c2)); // ムーブコンストラクタ std::string str1 = "abc"; std::string str2 = str1; // str2をコピー std::string str3 = std::move(str1); // str3にstr1の内容をムーブする。以降str1の内容は不定となる std::string str4(std::move(str3)); // str4にstr3の内容をムーブする。以降str3の内容は不定となる return 0; } </syntaxhighlight> <br><br> == ユニバーサル参照(&&で左辺値も束縛できる特別な例外) == <code>auto</code>変数や<code>template</code>変数の<code>&&</code>による参照は、右辺値だけでなく、左辺値も束縛できる。<br> <syntaxhighlight lang="c++"> // []は束縛対象(参照元), <>はコピーされた値 template <typename T> void g(T&& urefa) {} int x = 1; // xは左辺値 1は右辺値 int& lref1 = x; // lref1が左辺値参照 xは左辺値 const int& clref1 = x; // clref1はconst左辺値参照 xは左辺値 int&& rref1 = 1; // rref1が右辺値参照[1] 1は右辺値 // int&& rref2 = x; // コンパイルエラー // rref2が右辺値参照 lref1が左辺値参照 // int&& rref3 = lref1; // コンパイルエラー // rref3が右辺値参照 lref1が左辺値参照 // int&& rref4 = rref1; // コンパイルエラー // rref4が右辺値参照 rref1が右辺値参照(左辺値) // int&& rref5 = clref1; // コンパイルエラー // rref5が右辺値参照 clref1がconst左辺値参照 auto&& uref1 = 1; // uref1がユニバーサル参照[1] 1は右辺値 auto&& uref2 = x; // uref2がユニバーサル参照[x] xは左辺値 auto&& uref3 = lref1; // uref3がユニバーサル参照[x] lref1が左辺値参照[x] auto&& uref4 = rref1; // uref4がユニバーサル参照[1] rref1が右辺値参照[1] auto&& uref5 = clref1; // uref5がユニバーサル参照[x] clref1がconst左辺値参照[x] g(1); // g()の引数はユニバーサル参照 1は右辺値 g(x); // g()の引数はユニバーサル参照 xは左辺値 g(lref1); // g()の引数はユニバーサル参照 lref1は左辺値参照 g(rref1); // g()の引数はユニバーサル参照 rref1が右辺値参照 g(clref1); // g()の引数はユニバーサル参照 clref1がconst左辺値参照 </syntaxhighlight> <br><br> == 完全転送 == 完全転送とは、ユニバーサル参照が束縛した値において、右辺値・左辺値の型情報も保持して転送すること。<br> 完全転送を行うには、<code>std::forward</code>メソッドを使用する。<br> <syntaxhighlight lang="c++"> // 左辺値参照関数 void ref(int& a) { std::cout<< "左辺値参照" <<std::endl; } // 右辺値参照関数 // 引数bは右辺値のみ受け取る void ref(int&& b) { std::cout << "右辺値参照" << std::endl; } // 左辺値参照関数が呼び出される template <typename T> void h(T&& uref) { ref(uref); } // 右辺値参照版・左辺値参照版のどちらが呼ばれるかは引数により変わる template <typename T> void h_with_forward(T&& uref) { ref(std::forward<T>(uref)); } std::string str = "abc"; int x = 1; h(std::move(str)); // std::move(str)は右辺値 出力:左辺値参照 h(1); // 1は右辺値 出力:左辺値参照 h(x); // xは左辺値 出力:左辺値参照 h_with_forward(std::move(str)); // std::move(str)は右辺値 出力:右辺値参照 h_with_forward(1); // 1は右辺値 出力:右辺値参照 h_with_forward(x); // xは左辺値 出力:左辺値参照 </syntaxhighlight> <br><br> == 対応表 == 下表に、変数Aに変数Bを代入する時(A = B)の対応表を示す。<br> <center> {| class="wikitable" style="background-color:#fefefe;" |- ! style="background-color:#00ffff;" | A\B ! style="background-color:#00ffff;" | 左辺値 ! style="background-color:#00ffff;" | const左辺値 ! style="background-color:#00ffff;" | 左辺値参照 ! style="background-color:#00ffff;" | const左辺値参照 ! style="background-color:#00ffff;" | 右辺値 ! style="background-color:#00ffff;" | 右辺値参照 ! style="background-color:#00ffff;" | ユニバーサル参照 |- | 左辺値 || コピー || コピー || コピー || コピー || コピー || コピー || コピー |- | const左辺値 || コピー || コピー || コピー || コピー || コピー || コピー || コピー |- | 左辺値参照 || 束縛 || × || 束縛 || × || × || 束縛 || 束縛 |- | const左辺値参照 || 束縛 || 束縛 || 束縛 || 束縛 || 束縛 || 束縛 || 束縛 |- | 右辺値参照 || × || × || × || × || 束縛 || × || × |- | ユニバーサル参照 || 束縛 || 束縛 || 束縛 || 束縛 || 束縛 || 束縛 || 束縛 |} </center> <br><br> __FORCETOC__ [[カテゴリ:C++]]
C++の基礎 - 左辺値 と 左辺値参照
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse