質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

1200閲覧

左辺値、右辺値について

退会済みユーザー

退会済みユーザー

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2022/09/27 13:24

編集2022/09/27 14:43

左辺値、右辺値の学習中なのですが、よく分からないところがありまして。

① 関数やコンストラクタの引数は、呼び出し元が右辺値でも、引数は、左辺値ですよね。
② &参照の引数は、参照元が右辺値の場合、左辺値参照になるのでしょうか?
③ 以下のようなコンストラクタで値をセットした場合、③-1のmoveで左辺値strをコンストラクタ引数nameへmove。③-2でコンストラクタ内の_nameへmove。しているとおも思いますが、なぜmoveではなくfowordを使うのでしょうか?

C++

1struct Test 2{ 3 std::string name; 4 Test( std::string&& _name ) 5 : name( std::forward<std::string>( _name ) ) // ③−2 6 { 7 } 8} 9 10int main( void ) 11{ 12 std::string str = "AAA"; 13 Test sample( std::move(str) ); // ③−1 14 15 return 0; 16}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2022/09/27 13:46

> ③ 以下のようなコンストラクタで値をセットした場合 当該コードが文法的に成立していないようです。
kazuma-s

2022/09/27 14:26

foword って何ですか? <utility> の forward のつもりですか? コンストラクタのメンバ初期化子リストの書き方を間違っていませんか?
退会済みユーザー

退会済みユーザー

2022/09/27 14:38

失礼しました。修正させていただきました。
guest

回答3

0

① 関数やコンストラクタの引数は、呼び出し元が右辺値でも、引数は、左辺値ですよね。

はい。右辺値/左辺値という区分は、変数や引数に対してではなく 式(expression) に対して判断されるものです。

よく混同されていますが「左辺値参照/右辺値参照などの型情報(Type)」と「左辺値/右辺値の区分(Value Category)」は一義的には独立した概念です。

引数_nameを使った式_nameは、型(Type)が右辺値参照(std::string&&)であっても、値の区分(Value Category)としては左辺値(lvalue)になります。


② &参照の引数は、参照元が右辺値の場合、左辺値参照になるのでしょうか?

コンストラクタや関数の仮引数型としての「左辺値参照型(X&)」「const左辺値参照型(const X&)」「右辺値参照型(X&&)」と、呼び出し側で実引数に渡す式の「左辺値」「右辺値」の関係を整理すると、下表のようになります。

仮引数の型左辺値右辺値
X&×
const X&
X&&×

表中"×"箇所はコンパイルエラーになります。


③ 以下のようなコンストラクタで値をセットした場合、③-1のmoveで左辺値strをコンストラクタ引数nameへmove。③-2でコンストラクタ内の_nameへmove。しているとおも思いますが、なぜmoveではなくfowordを使うのでしょうか?

ご指摘通り③-1と③-2はいずれもムーブ処理が行われます。テンプレートパラメータを使わないstd::forward<std::string>(e)という記述は無用に混乱を招くだけですから、素直にstd::move(e)と記述するほうが好ましいと思います。

テンプレート関数std::forward<T>の役割は「条件付きムーブ(Conditional Move)」、つまり呼出側の実引数から推論されたテンプレートパラメータTに応じてコピー/ムーブ動作を切り替える仕組みです。Tstd::stringといった具象型を指定する(Non-Conditional)のであれば、直接的にstd::moveと記述する方がソースコードがシンプルになります。

投稿2022/09/29 05:49

編集2022/09/29 06:27
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

① 関数やコンストラクタの引数は、呼び出し元が右辺値でも、引数は、左辺値ですよね。

引数がというよりも右辺値参照は左辺値でもあります。

参照ではない仮引数の扱いはほぼ自動変数と同じで、つまり左辺値です。

② &参照の引数は、参照元が右辺値の場合、左辺値参照になるのでしょうか?

右辺値を const な左辺値参照で受け取ることは出来るようになっています。 (const ではない左辺値参照では受け取れません。) 実引数が右辺値であっても左辺値であっても区別なく左辺値参照です。

cpp

1void foo(int&) {} 2void bar(const int&) {} 3 4int main(void) { 5 foo(1); // これは駄目 6 bar(1); // これは通る 7}

これは右辺値参照の概念が C++ に導入される前の時代の必要性から導入された変則的なルールなのでこういうものだと覚えておくしかしょうがないですね。

なぜ move ではなく forword

この場合は move でも forword でも結果的な挙動は同じように思うので単に書いた人のクセか規約じゃないでしょうか。

普通はテンプレート以外で forword を使う必要はありませんが。

投稿2022/09/27 16:33

編集2022/09/27 16:36
SaitoAtsushi

総合スコア5444

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/09/27 22:37

>引数がというよりも右辺値参照は左辺値でもあります。 というのは、constをつけた場合ってことですね。 今回の場合は、moveでもfowordでもどっちでも同じ挙動。通常はforwordはテンプレート以外では使わない、ということで理解しました。 分かりやすく、ありがとうございます。
SaitoAtsushi

2022/09/28 06:35

いいえ。 const は関係ないです。 右辺値参照は左辺値であると述べています。 右辺値参照も左辺値参照も左辺値なのでその参照を関数の実引数として渡すと左辺値として受け取ってしまいます。 この挙動は std::forward の必要性とも関連してくるのでそのあたりを学習すれば自然に理解できると思いますが……。 #include <iostream> const char* foo(int&) { return "lvalue reference"; } const char* foo(int&&) { return "rvalue reference"; } int main(void) { int && x = 1; // 変数 x は右辺値参照だが…… std::cout << foo(x) << std::endl; // foo(int&&) ではなく foo(int&) が呼ばれる }
guest

0

② &参照の引数は、参照元が右辺値の場合、左辺値参照になるのでしょうか?

右辺値から、constな左辺値参照は作ることができます(参照が非constの場合は不可能です)。

投稿2022/09/27 13:42

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/09/27 22:31

ありがとうございます。 確かに参照の場合はそういう動きでした。右辺値とか全然知らずに使ってましたが、そういうことだったんですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問