29

構造体を返す関数呼び出しは右辺値式ですが、そのメンバーはどうでしょうか?
このコードは私の g++ コンパイラでうまく動作しますが、gcc は「代入の左オペランドとして左辺値が必要です」というエラーを出します:

struct A
{
    int v;
};

struct A fun()
{
    struct A tmp;
    return tmp;
}

int main()
{
    fun().v = 1;
}

gcc はfun().v右辺値として扱いますが、それは理解できます。
しかし、g++ は代入式が間違っているとは考えていません。それは fun1().v が C++ の左辺値であることを意味しますか?
問題は、C++98/03 標準を検索したところ、fun().v左辺値か右辺値かについては何もわかりませんでした。
それで、それは何ですか?

4

6 に答える 6

16

右辺値式のメンバーは右辺値です。

5.3.5 [expr.ref] の標準状態:

E2 が「T への参照」型を持つと宣言されている場合、E1.E2 は左辺値 [...] - E2 が非静的データ メンバーであり、E1 の型が「cq1 vq1 X」であり、 E2 の型が「cq2 vq2 T」の場合、式は最初の式で指定されたオブジェクトの名前付きメンバーを指定します。E1 が左辺値の場合、E1.E2 は左辺値です。

于 2010-02-08T12:20:31.767 に答える
2

編集:わかりました、私はついに標準から何かを得たと思います:

組み込みの代入演算子を持つv型であることに注意してください。int

13.3.1.2 式の演算子

4 組み込み代入演算子の場合、左オペランドの変換は次のように制限されます。 — 左オペランドを保持するための一時変数は導入されず、[...]

fun1()参照を返す必要があります。関数の非参照/ポインターの戻り値の型は右辺値です。

3.10 左辺値と右辺値

5 左辺値参照を返さない関数を呼び出した結果は右辺値 [...]

したがって、fun1().vは右辺値です。

8.3.2 参考文献

2 & を使用して宣言された参照型は左辺値参照と呼ばれ、&& を使用して宣言された参照型は右辺値参照と呼ばれます。左辺値参照と右辺値参照は異なる型です。

于 2010-02-08T08:12:33.703 に答える
0

代入式で右辺値を左辺値として使用することについて、gcc にはほとんど問題がない傾向があることに気付きました。たとえば、これは問題なくコンパイルされます。

class A {
};

extern A f();

void g()
{
   A myA;
   f() = myA;
}

なぜそれが合法で、これが合法でないのか (つまり、コンパイルしないのか) は、本当に私を混乱させます:

extern int f();

void g()
{
   f() = 5;
}

IMHO、標準委員会は、左辺値、右辺値、およびそれらを使用できる場所に関して説明する必要があります。これが、私がrvalues に関するこの質問に非常に興味を持っている理由の 1 つです。

于 2010-02-08T08:19:59.200 に答える
0

構造体/クラスに参照メンバーが含まれていない場合に備えて、コンパイラーがデフォルトのコンストラクター、デフォルトのコピーコンストラクター、およびデフォルトのコピー代入演算子を生成することを考えると、それは明らかです。次に、標準では、一時オブジェクトでメンバー メソッドを呼び出すことが許可されていると考えてください。つまり、非 const 一時オブジェクトで非 const メンバーを呼び出すことができます。

次の例を参照してください。

struct Foo {};
Foo foo () {
    return Foo();
}

struct Bar {
private:
    Bar& operator = (Bar const &); // forbid
};
Bar bar () {
    return Bar();
}
int main () {
    foo() = Foo(); // okay, called operator=() on non-const temporarie
    bar() = Bar(); // error, Bar::operator= is private
}

あなたが書くなら

struct Foo {};
const Foo foo () { // return a const value
    return Foo();
}

int main () {
    foo() = Foo(); // error
}

つまり、関数 foo() がconst一時を返すようにすると、コンパイル エラーが発生します。

例を完成させるために、const temporarie のメンバーを呼び出す方法を次に示します。

struct Foo {
    int bar () const { return 0xFEED; }
    int frob ()      { return 0xFEED; }
};
const Foo foo () {
    return Foo();
}

int main () {
    foo().bar(); // okay, called const member method
    foo().frob(); // error, called non-const member of const temporary
}

現在の式内にあるように一時的なライフタイムを定義できます。そのため、メンバー変数を変更することもできます。できなかった場合は、非 const メンバー メソッドを呼び出すことができる可能性よりも、不条理に導かれるでしょう。

編集:そして、ここに必要な引用があります:

12.2 一時オブジェクト:

  • 3) [...] 一時オブジェクトは、それらが作成されたポイントを (レキシカルに) 含む完全式 (1.9) を評価する最後のステップとして破棄されます。[...]

そして(またはより良い、前に)

3.10 左辺値と右辺値:

  • 10) オブジェクトを変更するには、オブジェクトの左辺値が必要です。ただし、特定の状況下では、クラス型の右辺値を使用してその参照先を変更することもできます。[例: オブジェクト (9.3) に対して呼び出されるメンバー関数は、オブジェクトを変更できます。]

使用例: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Parameter

于 2010-02-08T09:52:06.230 に答える
-2

あなたのコードにはシーンがありません。返された構造体はスタックに割り当てられるため、代入結果はすぐに失われます。

関数は、次の方法で A の新しいインスタンスを割り当てる必要があります。

new A()

この場合、より良い署名

A* f(){ ...

または、既存のインスタンスを返します。次に例を示します。

static A globalInstance;
A& f(){ 
  return globalInstance;
}
于 2010-02-08T08:16:42.500 に答える