左辺値と右辺値について この説明 を読んでいると、次のコード行が突き出ました:
int& foo();
foo() = 42; // OK, foo() is an lvalue
私はg ++で試しましたが、コンパイラは「foo()への未定義の参照」と言います。私が追加した場合
int foo()
{
return 2;
}
int main()
{
int& foo();
foo() = 42;
}
コンパイルは問題ありませんが、実行すると セグメンテーションエラー が発生します。ラインだけ
int& foo();
コンパイルも実行も問題なく実行できます。
このコードはどういう意味ですか?関数呼び出しに値を割り当てるにはどうすればよいですか?また、なぜ右辺値ではないのですか?
説明は、有効なfoo
への左辺値参照を返すint
の合理的な実装があると想定しています。
そのような実装は次のとおりです。
int a = 2; //global variable, lives until program termination
int& foo() {
return a;
}
これで、foo
は左辺値参照を返すため、次のように戻り値に何かを割り当てることができます。
foo() = 42;
これにより、グローバルa
が値42
で更新されます。これは、変数に直接アクセスするか、foo
を再度呼び出すことで確認できます。
int main() {
foo() = 42;
std::cout << a; //prints 42
std::cout << foo(); //also prints 42
}
他のすべての答えは、関数内で静的を宣言します。それはあなたを混乱させるかもしれないと思うので、これを見てみましょう:
int& highest(int & i, int & j)
{
if (i > j)
{
return i;
}
return j;
}
int main()
{
int a{ 3};
int b{ 4 };
highest(a, b) = 11;
return 0;
}
highest()
は参照を返すため、値を割り当てることができます。これが実行されると、b
は11に変更されます。a
がたとえば8になるように初期化を変更した場合、a
は11に変更されます。他の例とは異なり、実際に目的を果たす可能性があります。
int& foo();
int
への参照を返すfooという名前の関数を宣言します。その例でできないのは、コンパイルできる関数の定義を提供することです。使用する場合
int & foo()
{
static int bar = 0;
return bar;
}
これで、bar
への参照を返す関数ができました。 barはstatic
であるため、関数の呼び出し後も有効なので、参照を安全に返すことができます。今なら
foo() = 42;
何が起こるかというと、参照に割り当てられ、参照はbar
の単なるエイリアスであるため、42をbar
に割り当てます。次のように関数を再度呼び出すと
std::cout << foo();
bar
を上記の値に設定したため、42が出力されます。
int &foo();
は、戻り型int&
でfoo()
という関数を宣言します。本体を提供せずにこの関数を呼び出すと、未定義の参照エラーが発生する可能性があります。
2回目の試行では、関数int foo()
を提供しました。これは、int& foo();
で宣言された関数とは異なる戻り型を持ちます。そのため、一致しない同じfoo
の2つの宣言があります。これは、1つの定義ルールに違反して未定義の動作を引き起こします(診断は不要です)。
機能するものについては、ローカル関数宣言を取り出します。あなたが見たように、それらは静かな未定義の振る舞いをもたらすことがあります。代わりに、関数の外部でのみ関数宣言を使用してください。プログラムは次のようになります。
int &foo()
{
static int i = 2;
return i;
}
int main()
{
++foo();
std::cout << foo() << '\n';
}
int& foo();
は、int
への参照を返す関数です。指定した関数は、参照なしでint
を返します。
やってもいい
int& foo()
{
static int i = 42;
return i;
}
int main()
{
int& foo();
foo() = 42;
}
int & foo();
は、foo()
が変数への参照を返すことを意味します。
次のコードを検討してください。
#include <iostream>
int k = 0;
int &foo()
{
return k;
}
int main(int argc,char **argv)
{
k = 4;
foo() = 5;
std::cout << "k=" << k << "\n";
return 0;
}
このコードは次を印刷します:
$ ./a.out k = 5
foo()
はグローバル変数k
への参照を返すためです。
修正したコードでは、戻り値を参照にキャストしていますが、これは無効です。
そのコンテキストでは、&は参照を意味するため、fooはintではなくintへの参照を返します。
ポインタを使用したことがあるかどうかはわかりませんが、同様の考えです。実際に関数から値を返すのではなく、メモリ内の場所を見つけるために必要な情報を渡しますintです。
つまり、関数呼び出しに値を割り当てるのではなく、関数を使用して参照を取得し、参照される値に新しい値を割り当てています。すべてが一度に起こると考えるのは簡単ですが、実際にはコンピューターはすべてを正確な順序で実行します。
あなたが疑問に思っている場合-segfaultを取得している理由は、数値リテラル '2'を返しているためです-したがって、const intを定義してから変更しようとした場合に取得する正確なエラーです値。
ポインタと動的メモリについてまだ学習していない場合は、一度にすべてを学習しない限り理解が難しいと思われる概念がいくつかあるため、まずその方法をお勧めします。
リンクされたページのサンプルコードは、単なるダミー関数宣言です。コンパイルはしませんが、関数を定義しておけば、一般的に機能します。この例は、「このシグネチャを持つ関数があれば、そのように使用できます」という意味でした。
この例では、foo
は明らかに署名に基づいて左辺値を返しますが、左辺値に変換される右辺値を返します。これは明らかに失敗すると判断されます。あなたができる:
int& foo()
{
static int x;
return x;
}
次のように言うと、xの値を変更することで成功します。
foo() = 10;
あなたが持っている関数foo()は、整数への参照を返す関数です。
したがって、最初にfooが5を返し、後でメイン関数でfoo() = 10;
と言い、その後fooを出力すると、5ではなく10が出力されます。
私はそれが理にかなっていることを願っています:)
プログラミングも初めてです。このような質問にあなたが思うようになるのは興味深いことです! :)