web-dev-qa-db-ja.com

C ++:戻り値はL値ですか?

このコードを検討してください:

_struct foo
{
  int a;
};

foo q() { foo f; f.a =4; return f;}

int main()
{
  foo i;
  i.a = 5;
  q() = i;
}
_

Clangでさえ、コンパイラはそれについて文句を言いません。 q() = ...行が正しいのはなぜですか?

72
John

いいえ、関数の戻り値は、それが参照(C++ 03)である場合に限り、l値です。 (5.2.2 [expr.call]/10)

返される型が基本型である場合、これはコンパイルエラーになります。 (5.17 [expr.ass]/1)

これが機能する理由は、クラスタイプのr値でメンバー関数(非constメンバー関数でも)を呼び出すことが許可されており、fooの割り当てが実装定義のメンバー関数であるためです。 : foo& foo::operator=(const foo&)。条項5の演算子の制限は、組み込み演算子、(5 [expr]/3)にのみ適用されます。オーバーロード解決によってオペレーターのオーバーロードされた関数呼び出しが選択された場合、その関数呼び出しの制限が適用されます。代わりに。

これが、クラスタイプのオブジェクトをconstオブジェクト(例:const foo q();)として返すことが推奨される場合がある理由ですが、これはC++ 0xで悪影響を及ぼし、移動セマンティクスを阻害する可能性があります。彼らがすべきように働くことから。

66
CB Bailey

構造体を割り当てることができ、q()struct fooのコピーを返すため、返された構造体を指定された値に割り当てます。

この場合、構造体は後でスコープから外れ、そもそも構造体への参照を保持しないため、(この特定のコードでは)何もできなかったため、これは実際には何もしません。

これはより理にかなっています(ただし、実際には「ベストプラクティス」ではありません)

struct foo
{
  int a;
};

foo* q() { foo *f = new malloc(sizeof(foo)); f->a = 4; return f; }

int main()
{
  foo i;
  i.a = 5;

  //sets the contents of the newly created foo
  //to the contents of your i variable
  (*(q())) = i;
}
8
Chad

これの1つの興味深いアプリケーション:

void f(const std::string& x);
std::string g() { return "<tag>"; }

...

f(g() += "</tag>");

ここで、g() +=は一時を変更します。これは、g()の戻り値に割り当てられたヒープに、+を収容するのに十分な予備容量がある可能性があるため、</tag>で追加の一時を作成するよりも高速です。

それが実行されるのを見てください ideone.comでGCC/C++ 11を使用

さて、どのコンピューティング初心者が最適化と悪について何かを言いましたか...? ;-]。

6
Tony Delroy