C ++では「int&foo()」は何を意味しますか?


114

左辺値と右辺値に関するこの説明を読んでいると、次のコード行が突き刺さっていました。

int& foo();
foo() = 42; // OK, foo() is an lvalue

私はそれをg ++で試しましたが、コンパイラは「foo()への未定義の参照」と言っています。追加した場合

int foo()
{
  return 2;
}

int main()
{
  int& foo();
  foo() = 42;
}

正常にコンパイルされますが、実行するとセグメンテーション違反が発生します。ただの線

int& foo();

コンパイルするだけで問題なく実行されます。

このコードはどういう意味ですか?どのように値を関数呼び出しに割り当てることができますか、そしてそれはなぜ右辺値ではないのですか?


25
関数呼び出しに値を割り当てるのではなく、関数が返す参照に値を割り当てます
フレデリックハミディ

3
@FrédéricHamidiは、返された参照が参照しているオブジェクトに値を割り当てます
MM

3
はい、それはオブジェクトのエイリアスです。参照はオブジェクトではありません
MM

2
@RoyFalkのローカル関数宣言はポックスです。個人的には、それらが言語から削除されることを嬉しく思います。(もちろんラムダを除く)
MM

4
これにはすでにGCCバグがあります
jotik

回答:


168

説明では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
}

operator []のように合理的ですか?または他のメンバーがメソッドにアクセスしますか?
Jan Dorniak 16

8
屈折に関する混乱を考えると、サンプルから静的ローカル変数を削除するのがおそらく最善です。初期化は不必要な複雑さを追加します。これは学習のハードルです。グローバル化するだけです。
Mooing Duck

72

他のすべての答えは、関数内でstaticを宣言します。私はそれがあなたを混乱させるかもしれないと思うので、これを見てください:

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に初期化を変更した場合、11 aに変更されます。これは、他の例とは異なり、実際に目的を果たす可能性のあるコードです。


5
int a = 3の代わりにint a {3}と書く理由はありますか?この構文は私には適切ではないようです。
Aleksei Petrenko

6
@AlexPetrenkoそれは普遍的な初期化です。たまたま手にした例で使っていました。それについて不適切なことは何もありません
ケイトグレゴリー

3
これが何なのか知っています。a = 3はとてもすっきりした感じです。個人の好み)
アレクセイペトレンコ2016

関数内でstaticを宣言すると一部の人々が混乱する可能性があるのと同様に、普遍的な初期化を使用する可能性も高いと思います。
エリックカーチン

@AlekseiPetrenko int a = 1.3の場合、暗黙的にa = 1に変換されます。一方、int {1.3}はエラーになります。つまり、変換を狭めます。
サトビク

32
int& foo();

への参照を返すfooという名前の関数を宣言しますint。この例で失敗するのは、コンパイルできる関数の定義を提供することです。使うなら

int & foo()
{
    static int bar = 0;
    return bar;
}

これで、への参照を返す関数ができましたbar。barはstatic関数の呼び出し後も存続するため、参照を返すのは安全です。今私たちがするなら

foo() = 42;

bar参照に割り当て、参照はの単なるエイリアスであるため、42 を割り当てbarます。次のように関数を再度呼び出すと

std::cout << foo();

bar上記に設定したので、42 を出力します。


15

int &foo();foo()戻り値の型で呼び出される関数を宣言しますint&。本文を提供せずにこの関数を呼び出すと、未定義の参照エラーが発生する可能性があります。

2回目の試行では、関数を提供しましたint foo()。これは、によって宣言された関数とは異なる戻り型を持っていint& foo();ます。そのfooため、一致しない同じ宣言が2つあります。これは、1つの定義ルールに違反しており、未定義の動作を引き起こします(診断は不要)。

機能するものについては、ローカル関数宣言を削除します。あなたが見たように、それらはサイレントな未定義の動作につながる可能性があります。代わりに、関数の外部でのみ関数宣言を使用してください。プログラムは次のようになります。

int &foo()
{
    static int i = 2;
    return i;
}  

int main()
{
    ++foo();  
    std::cout << foo() << '\n';
}

10

int& foo();への参照を返す関数intです。提供された関数はint参照なしで戻ります。

あなたがするかもしれません

int& foo()
{
    static int i = 42;
    return i;
}

int main()
{
    int& foo();
    foo() = 42;
}

7

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

改訂されたコードでは、戻り値を参照にキャストしていますが、これは無効になります。


5

そのコンテキストでは、&は参照を意味します。したがって、fooはintではなくintへの参照を返します。

ポインタを操作したことがあるかどうかはわかりませんが、同じような考え方です。実際に関数から値を返すのではなく、代わりにメモリ内の場所を見つけるために必要な情報を渡しています。 intです。

つまり、関数呼び出しに値を割り当てているわけではありません-関数を使用して参照を取得し、参照されている値を新しい値に割り当てています。すべてが一度に発生すると考えるのは簡単ですが、実際にはコンピュータはすべてを正確な順序で実行します。

疑問に思っている場合-segfaultが発生する理由は、数値リテラル '2'が返されているためです。つまり、const intを定義し、その値を変更しようとすると、エラーが発生します。値。

ポインタと動的メモリについてまだ学習していない場合は、一度に学習しないと理解が難しいと思う概念がいくつかあるので、まずお勧めします。


4

リンク先のページのサンプルコードは、ダミーの関数宣言です。コンパイルはできませんが、関数を定義しておけば、通常は機能します。この例は、「このシグネチャを持つ関数があれば、そのように使用できる」ことを意味しています。

あなたの例でfooは、署名に基づいて左辺値を明確に返しますが、左辺値に変換される右辺値を返します。これは明らかに失敗すると判断されます。あなたがすることができます:

int& foo()
{
    static int x;
    return x;
}

そして言うとき、xの値を変更することで成功します:

foo() = 10;

3

持っている関数foo()は、整数への参照を返す関数です。

したがって、最初にfooが5を返し、後でメイン関数でと言ってfoo() = 10;、fooを出力すると、5ではなく10が出力されます。

私はそれが理にかなっていると思います:)

プログラミングも初めてです。あなたが考えるようなこのような質問を見るのは興味深いです!:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.