6
#include<iostream>
using namespace std;

int &fun()
{
  static int x = 10;
  return x;
}
int main()
{
   fun() = 30;
   cout << fun();
   return 0;
}

関数 fun() は参照によって値を返しますが、 main() メソッドでは関数に int を割り当てています。理想的には、コンパイラは lvalue required のようなエラーを表示する必要がありますが、上記の場合、プログラムは正常に動作します。なぜそうなのですか?

4

7 に答える 7

7

「関数が何かを返す」と言うのは、ゆるくてずさんな言葉です。使い方が分かっていれば省略形としては問題ありませんが、この場合は混乱します。

それについて考えるより正しい方法は、関数呼び出し式を評価することです。そうすることで価値が生まれます。値は、右辺値または左辺値 (モジュロ詳細) のいずれかです。

Tオブジェクト型で、戻り値の型を持つ関数を評価すると、右辺T値である型の値が得られます。T一方、関数が return typeT &を持っている場合、左辺値である type の値を取得します(値はステートメントT内の参照にバインドされたものです)。return

于 2013-09-23T17:02:54.937 に答える
4

参照を返すことは非常に便利です。

たとえば、それは何をするかstd::map::operator[]です。そして、あなたが書く可能性を気に入ってくれることを願っていますmy_map[key] = new_value;.

通常の(演算子ではない)関数が参照を返す場合、それに割り当てても問題ありません。これを禁止する理由はありません。

本当に必要な場合は、 aconst X&を返すか、代わりに返すことで、代入を防ぐことができます。X

于 2013-09-23T17:11:18.483 に答える
2

他の回答に加えて、次のコードを検討してください。

SomeClass& func() { ... }

func().memberFunctionOfSomeClass(value);

これは完全に自然なことであり、コンパイラがこれでエラーを返すと思っていたら、私は非常に驚かれることでしょう。

さて、some_obj = value;実際に舞台裏で起こっていることを書くときは、あなたが呼び出すことですsome_obj.operator =(value);. Andoperator =()はクラスのもう 1 つのメンバー関数であり、 と同じですmemberFunctionOfSomeClass()

要約すると、次のようになります。

func() = value;
// equivalent to
func().operator =(value);
// equivalent to
func().memberFunctionOfSomeClass(value);

もちろん、これは単純化しすぎており、この表記法は のような組み込み型には適用されませんint(ただし、同じメカニズムが使用されます)。

これが、他の人がlvalueに関してすでに説明したことをよりよく理解するのに役立つことを願っています。

于 2013-09-23T17:20:33.403 に答える
2

その関数の結果が左辺値であるため、機能します。参照は左辺値です。基本的に、関数から非 const 参照を返すことの全体的なポイントは、それに代入できることです (または、参照されたオブジェクトの他の変更を実行できます)。

于 2013-09-23T17:12:25.360 に答える
2

私も同様のコードに困惑しました-最初は。それは「なぜ私が関数呼び出しに値を代入するのか、なぜコンパイラーはそれに満足するのか?」ということでした。私は自問自答しました。しかし、「背後」で何が起こっているかを見ると、それは理にかなっています。


cppや他の人が指摘したように、左辺値はアドレスを持つ「メモリ ロケーション」であり、値を割り当てることができます。lvalues と rvalues のトピックについては、インターネットで詳細を見つけることができます。

関数を見ると:

int& fun()
{
    static int x = 10;
    return x;
}

& を型に移動したので、int への参照を返していることがより明確になります。左辺値であるx
がある ことがわかります。これにはアドレスがあり、それに割り当てることができます。また、静的であるため、特別です。静的でない場合、変数の有効期間 (スコープ) は、関数を終了するときにスタックの巻き戻しで終了し、参照は、宇宙に存在するブラック ホールを指す可能性があります。ただし、xは静的であるため、関数を離れた後も (そして再び関数に戻ったときも) 存在し、関数の外部からアクセスできます。

int への参照を返しています。 x を返すため、 xへの参照です。次に、参照を使用して、関数の外側でxを変更できます。そう:

int main()
{
    fun();

関数を呼び出すだけです。変数x (fun 関数のスコープ内) が作成され、値 10 が割り当てられます。関数が終了した後でもアドレスと値は存在しますが、アドレスがないため、その値を使用できません。

    fun() = 30;

関数を呼び出して、xの値を変更します。x値は、関数によって返される参照を介して変更されます。:関数は最初に呼び出され、関数呼び出しが完了した後にのみ呼び出され、次に割り当てが行われます。

    int& reference_to_x = fun(); // note the &

ここで、関数によって返されたxへの参照を (最終的に) 保持します。これで、最初に関数を呼び出さずにxを変更できます。( reference_to_xはおそらくxが fun 関数内に持っているのと同じアドレスを持つでしょう)

    int copy_of_x = fun(); // no & this time

今回は新しい int を作成し、(参照を介して) xの値をコピーするだけです。この新しい int には独自のアドレスがあり、reference_to_xのようにxを指していません。

    reference_to_x = 5;

参照を介してxに値 5を割り当てましたが、関数は呼び出しませんでした。copy_of_xは変更されません。

    copy_of_x = 15;

新しい int を値 15 に変更しました。copy_of_xには独自のアドレスがあるため、 xは変更されません。

}


6502と他の人が指摘したように、コンテナーとカスタム オーバーライドで多くの参照を返すことで同様のアプローチを使用します。

std::map<std::string, std::string> map = {};

map["hello"] = "Ahoj";
// is equal to
map.operator[]("hello") = "Ahoj"; // returns reference to std::string
// could be done also this way
std::string& reference_to_string_in_map = map.operator[]("hello");
reference_to_string_in_map = "Ahoj";

使用する map 関数は、次のような宣言を持つことができます。

std::string& map::operator[]( const std::string& key ); // returns reference

マップに「格納」した文字列へのアドレスがないため、マップのこのオーバーライドされた関数を呼び出し、キーを渡して、マップがアクセスしたい文字列を認識し、その文字列への参照を返します。を使用して値を変更できます。: ここでも、関数が最初に呼び出され、関数が完了した後にのみ (map が正しい文字列を見つけ、それへの参照を返した)、代入が行われます。それは fun() = 10 のようなもので、より美しいだけです...

これが、他の回答を読んでもまだすべてを理解していない人の助けになることを願っています...

于 2014-08-17T14:14:12.797 に答える
0

L-value はlocator-valueです。住所があるということです。参照には明らかにアドレスがあります。値でfun() から戻る場合に取得できる必要な左辺値:

#include<iostream>
using namespace std;

int fun()
{
  static int x = 10;
  return x;
}
int main()
{
   fun() = 30;
   cout << fun();
   return 0;
}
于 2013-09-23T17:14:50.480 に答える