const ref и rvalue в D - PullRequest
       44

const ref и rvalue в D

5 голосов
/ 08 августа 2011

Код

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 не может связываться с rvalue? Это нормально?
  2. Нужно ли возвращать ref CustomReal или const ref CustomReal из opBinary(), чтобы решить эту проблему? Это нормально?
  3. Правильно ли возвращать ссылку на локальный объект, созданный в стеке? ref CustomReal Create() { return CustomReal(0.0); }

Ответы [ 3 ]

4 голосов
/ 09 августа 2011

Единственная разница между ref и const ref заключается в том, что const ref - это const, а ref - нет.Оба должны принимать переменную.Ни один не может занять временное.Это отличается от C ++, где const 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)

Компилятор в настоящее время слишком требователен к точной сигнатуре opEquals для структур (некоторые другие функции, такие как toString, имеют аналогичные проблемы).Это известная проблема, которая, вероятно, будет решена в ближайшем будущем.Однако пока просто объявите обе перегрузки opEquals.Если вы сравните CustomReal с переменной, то будет использоваться версия const ref, а если вы сравните CustomReal с временной, то будет использоваться другая версия.Но если у вас есть оба, с вами все будет в порядке.

Теперь, почему

assert(a == CustomReal(123.123456));

работает, а

assert(a == Create());  

нет, я не уверен,Я бы на самом деле ожидал, что оба из них потерпят неудачу, учитывая, что const ref не может занять временное значение, но по какой-то причине компилятор принимает его здесь - это, вероятно, связано с тем, как он обрабатывает opEquals спец.Несмотря на это, как я уже сказал, есть некоторые проблемы с opEquals и структурами, которые необходимо устранить, и, надеюсь, это произойдет в ближайшее время.Но в то же время объявление обеих перегрузок opEquals, кажется, делает свое дело.

EDIT: Похоже, что причина того, что

assert(a == CustomReal(123.123456));

работает, и

assert(a == Create());

не из-за того факта (по причинам, которые я не понимаю), структурный литерал считается lvalue, тогда как возвращаемое значение функции, которая не является ref, равно (неудивительно)))С ним связано пара из отчетов об ошибках , утверждающих, что структурные литералы должны быть значениями r, но, очевидно, они являются lvalues ​​по замыслу (что сбивает меня с толку).В любом случае, именно поэтому функция, которая принимает const ref, работает со структурным литералом, но не с возвращаемым значением функции.

3 голосов
/ 09 августа 2011

#1: Да, const ref не может привязаться к значениям.Андрей считает, что это было плохой идеей - разрешить это в C ++ IIRC.http://digitalmars.com/d/archives/digitalmars/D/const_ref_rvalues_103509.html#N103514

Странно, что структурные литералы считаются lvalues ​​в D2, поэтому это работает:

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

, тогда как при вызове следующего нет:

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

foo(bar());
2 голосов
/ 09 августа 2011

Для # 2 (и с расширением # 3): Нет, это будет недопустимо, поскольку это должна быть ссылка на объект, который только что вышел из области видимости.

...