web-dev-qa-db-ja.com

一時オブジェクトを参照として渡すことができません

これはごくわずかな例です。

_class Foo
{
public:
    Foo(int x) {};
};

void ProcessFoo(Foo& foo)
{
}

int main()
{
    ProcessFoo(Foo(42));
    return 0;
}
_

上記はVisualStudioで正常にコンパイルされますが、LinuxおよびMacではエラーが発生します。

上記をコンパイルすると、次のようになります。

_$ g++ -std=c++11 -c newfile.cpp

newfile.cpp: In function ‘int main()’:
newfile.cpp:23:23: error: invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
     ProcessFoo(Foo(42));
                       ^
newfile.cpp:14:6: note: in passing argument 1 of ‘void ProcessFoo(Foo&)’
 void ProcessFoo(Foo& foo)
_

私は3つの回避策を見つけました:

  1. ProcessFooの呼び出しにインライン一時変数を使用することは避けてください。

このような:

_Foo foo42(42);
ProcessFoo(foo42);
_
  1. ProcessFooはconst参照を取ります:void ProcessFoo(const Foo& foo)

  2. ProcessFooは、Fooに値を渡すだけです。 void ProcessFoo(Foo foo)

コンパイラが元のコードを禁止しているのはなぜですか? (それは何を防いでいますか)?コンパイラを満足させる上記の3つの回避策のそれぞれについてはどうですか? MSVCはそれを許可しますが、g ++は許可しませんか?

18
selbie

設計上、C++では、一時参照のみをconst参照、value、またはrvalue参照に渡すことができます。アイデアは、非const参照パラメーターを受け取る関数が、パラメーターを変更して呼び出し元に戻せるようにすることを示しているということです。一時的にそうすることは無意味であり、おそらくエラーです。

また、実行しているg ++のバージョンがわかりません。ここでは機能しません: http://coliru.stacked-crooked.com/a/43096cb398cbc97

15
Jay Miller

コンパイラが元のコードを禁止しているのはなぜですか?

規格で禁止されているため:

8.5.3参考文献5
.。
それ以外の場合、参照は非揮発性const型への左辺値参照(つまり、cv1はconst)であるか、参照は右辺値参照である必要があります。
[例:
double&rd2 = 2.0; //エラー:左辺値ではなく、参照はconstではありません
.。

'

それは何を防いでいますか?

関数呼び出し後に破棄されるオブジェクトを誤って変更しました。

コンパイラを満足させる上記の3つの回避策のそれぞれについてはどうですか?

1名前付きオブジェクトを作成し、3コピーを作成します。
2オブジェクトの存続期間が単純に延長され、同時にオブジェクトへの変更が防止されるため、機能します。

MSVCはそれを許可しますが、g ++は許可しませんか?

言語拡張だからです。 Property Pages->C/C++->Language->Disable Language Extensionsに移動して無効にすると、エラーが発生します。

11
imreal

コンパイラが元のコードを禁止しているのはなぜですか?

MSVCには、一時的なものを非定数の左辺値参照にバインドできるようにする拡張機能があります。もちろん、これは標準に準拠した機能ではないので、ポータブルにするためにそれを避けたいと思います。たとえば、これまで見てきたように、GCCとClangの最新バージョンでは機能しません。

コンパイラを満足させる上記の3つの回避策のそれぞれについてはどうですか?

C++ 03に戻ると、式は左辺値または右辺値のみでした。参照はオブジェクトの「左辺値」を指定することしかできなかったため、既存のオブジェクトをエイリアスする目的で使用されました。対照的に、右辺値は、それらが表示される式を超えて存在することはありません。また、参照の最終結果は通常、オブジェクトをコピーまたは変更することであり、たとえば55のような右辺値を変更することは言語にとってあまり意味がありません。

ルールを使用すると、右辺値を左辺値にバインドできます-constへの参照。この場合、一時の有効期間は参照の有効期間まで延長されます。オブジェクトを値で取得すると、オブジェクトがコピーされます。

C++ 11には、所有権を交換する目的で作成された右辺値参照とx値があります。これにより、constへの左辺値参照の有用性が低下します。さらに、値による取得は、それが右辺値である場合に移動を引き起こします。

3
0x499602D2

ProcessFooのプロトタイプを次のように宣言したら

void ProcessFoo(Foo& foo)

仮パラメータ「foo」はconst&によって渡されないため、変更される可能性があるため、意図を伝えています。

コールサイトでは、

ProcessFoo(Foo(42));

Foo(42)は、変更できない一時スタックオブジェクトを作成しています。メソッドに値渡しまたはref-to-const渡しを行っても問題ありません。

あなたが自分自身をリストしたように、それらの制約を満たすことは、コンパイラーを幸せにします。

  1. コンパイラによって生成されておらず、制御下にあるオブジェクトを提供しています。
  2. メソッドがオブジェクトのconst-nessを保証することをコンパイラーに通知します。
  3. この(一時的な)オブジェクトは値によって渡されるため、問題がないことをコンパイラーに通知します。
2
KalyanS