5

コード

struct CustomReal
{
   private real value;

   this(real value)
   {
      this.value = value;
   }

   CustomReal opBinary(string op)(CustomReal rhs) if (op == "+")
   {
      return CustomReal(value + rhs.value);
   }

   bool opEquals(ref const CustomReal x) const
   {
      return value == x.value; // just for fun
   }
}

// Returns rvalue 
CustomReal Create()
{
   return CustomReal(123.123456);
}

void main()
{
   CustomReal a = Create();
   assert(a == CustomReal(123.123456)); // OK. CustomReal is temporary but lvalue
   assert(a == Create());               // Compilation error (can't bind to rvalue)
   assert(a != a + a);                  // Compilation error (can't bind to rvalue)
}

コンパイルエラー

prog.d(31): Error: function prog.CustomReal.opEquals (ref const const(CustomReal) x) const is not callable using argument types (CustomReal)
prog.d(31): Error: Create() is not an lvalue

http://ideone.com/O8wFc

質問:

  1. const ref右辺値にバインドできないのはなぜですか? 大丈夫ですか?
  2. この問題を解決するには、戻る必要がありますref CustomRealか? 大丈夫ですか?const ref CustomRealopBinary()
  3. スタック上に作成されたローカル オブジェクトへの参照を返すのは正しいですか? ref CustomReal Create() { return CustomReal(0.0); }
4

3 に答える 3

4

との唯一の違いは、refあるconst refこととないことです。どちらも変数を取る必要があります。どちらも一時を取ることはできません。これは、一時値を含む任意の型の値を取るC++ とは異なります。const refconstrefconst T&T

opBinary返す変数がないため、refまたはを返すことはできません。const ref一時的に作成しています。についても同様ですCreate。また、ローカル変数への参照を返すことができないため、戻りたい値でローカル変数を作成しても役に立ちません。もう存在しない変数を参照することになります。

ここで行う必要があるのは、次の別のオーバーロードを追加することですopEquals:

bool opEquals(CustomReal x) const
{
    return value == x.value; // just for fun
}

これで、コードがコンパイルされます。

ただし、現在の状況を少し解決するopEquals 必要があることを指摘しておきます。opEquals現在持っているオーバーロードではなく、私が提供したオーバーロードしか持っていない場合、コードはコンパイルに失敗し、次のようなエラーが発生することに気付くでしょう。

prog.d(15): Error: function prog.CustomReal.opEquals type signature should be const bool(ref const(CustomReal)) not const bool(CustomReal x)

現在、コンパイラはopEqualsfor structs の正確なシグネチャについて非常にうるさいです (他のいくつかの関数 ( などtoString) にも同様の問題があります)。これは既知の問題であり、近い将来に解決される可能性があります。ただし、今のところ、 の両方のオーバーロードを宣言するだけですopEqualsCustomReal変数と比較する場合はconst refバージョンが使用され、CustomReal一時変数と比較する場合は別のバージョンが使用されます。でも、両方持っていれば大丈夫です。

さて、なぜ

assert(a == CustomReal(123.123456));

動作し、

assert(a == Create());  

わかりません。一時的に取ることができないことを考えると、実際には両方が失敗することを期待してconst refいますが、何らかの理由で、コンパイラーはここでそれを受け入れます-おそらくそれが特別なものをどのように扱うかに関係していますopEquals。とにかく、私が言ったように、解決する必要がある と 構造体にはいくつかの問題があり、opEqualsそれがすぐに起こることを願っています。しかし、当面は、両方のオーバーロードを宣言opEqualsすることでうまくいくようです。

編集:その理由は

assert(a == CustomReal(123.123456));

動作し、

assert(a == Create());

ないのは、構造体リテラルが左辺値と見なされるのに対し、(当然のことながら) 右辺値ではない関数の戻り値refが (当然のことながら) 右辺値であるという事実のためです。それに関連するバグレポートいくつかあり、構造体リテラルは右辺値であるべきだと主張していますが、どうやらそれらは設計上左辺値であるようです (これには困惑しています)。いずれにせよ、関数の戻り値ではなく、構造体リテラルを使用する関数が機能するのはそのためです。const ref

于 2011-08-09T04:41:34.080 に答える
3

#1: はい、const ref は右辺値にバインドできません。Andrei は、C++ IIRC でそれを許可するのは悪い考えだったと考えています。 http://digitalmars.com/d/archives/digitalmars/D/const_ref_rvalues_103509.html#N103514

奇妙なことに、D2 では構造体リテラルが左辺値としてカウントされるため、これは機能します。

struct A {}
void foo(ref A a) {}
void main()
{
    foo(A());
}

次の呼び出し中はしません:

static A bar()
{
    return A();
}

foo(bar());
于 2011-08-08T22:35:31.000 に答える
2

#2の場合(および拡張機能#3の場合):いいえ、スコープ外になっているオブジェクトへの参照である必要があるため、無効になります。

于 2011-08-08T20:34:14.143 に答える