ПЕРЕХОДНЫЕ ПРИСТАВКИ, непереходные приставки, rvalue и lvalues

Кто-либо мог объяснить детали с точки зрения rvalues, lvalues, ПЕРЕХОДНЫХ ПРИСТАВОК и непереходных приставок причина, почему первое выражение, отмеченное ниже, не в порядке, в то время как второе выражение, отмеченное ниже, в порядке? В моем понимании обоих интервалов () и () должен быть rvalues, нет?


struct A {};

int main()
{
  int i;
  A a;

  int() = i; //Not OK (error).
  A() = a; //OK.

  return 0;
}

21
задан anonymous 19 February 2010 в 02:54
поделиться

3 ответа

Значения R - это то, что вы получаете из выражений (полезное упрощение, взятое из стандарта C, но не сформулированное в стандарте C ++). Lvalue - это «значения локатора». Lvalues ​​можно использовать как rvalue. Ссылки всегда являются lvalue, даже если const.

Основное различие, о котором вы должны знать, можно свести к одному элементу: вы не можете взять адрес rvalue (опять же, не стандартное, а полезное обобщение правил). Или, другими словами, вы не можете установить точное местоположение для rvalue - если бы вы могли, то у вас было бы lvalue . (Однако вы можете связать константу & с rvalue, чтобы «исправить это на месте», а 0x радикально меняет правила.)

Пользовательские типы (UDT), однако, немного особенные: вы можете преобразовать любые rvalue в lvalue, если интерфейс класса это позволяет:

struct Special {
  Special& get_lvalue() { return *this; }
};
void f() {
  // remember "Special()" is an rvalue
  Special* p = &Special().get_lvalue(); // even though you can't dereference the
  // pointer (because the object is destroyed), you still just took the address
  // of a temporary

  // note that the get_lvalue() method doesn't need to operate on a const
  // object (though that would be fine too, if the return type matched)
}

Нечто подобное происходит с вашим A () = a , за исключением оператора присваивания, предоставляемого компилятором, чтобы преобразовать rvalue A () в * это . Процитируем стандарт 12.8 / 10:

Если определение класса не объявляет явно оператор присваивания копии, он объявляется неявно . Неявно объявленный оператор присваивания копии для класса X будет иметь форму

 X & X :: operator = (const X &) 
 

И затем он переходит к дополнительным уточнениям и спецификациям, но это важный бит здесь. Поскольку это функция-член, ее можно вызывать для rvalue, как и Special :: get_lvalue, как если бы вы написали A (). Operator = (a) вместо A () = a .

int () = 1 явно запрещен, как вы обнаружили, потому что в int не реализован operator = таким же образом. Однако это небольшое несоответствие между типами на практике не имеет значения (по крайней мере, то, что я обнаружил).


POD означает обычные старые данные и представляет собой набор требований, которые определяют, что использование memcpy эквивалентно копированию. Non-POD - это любой тип, для которого вы не можете использовать memcpy для копирования (естественная противоположность POD, здесь ничего не скрыто), как правило, это большинство типов, которые вы будете писать на C ++. Статус POD или не-POD не меняет ничего из вышеперечисленного и действительно является отдельной проблемой.

19
ответ дан 29 November 2019 в 21:49
поделиться

Насколько я понимаю, как int (), так и A () должны быть r-значениями, не так ли?

Верно, epxression T () всегда является r-значением для скалярных и определяемых пользователем типов T . Пока не используется const , выражение T () является изменяемым r-значением , если быть более точным.

Присваивание, включающее скалярные типы, требует изменяемого lvalue в левой части оператора присваивания. Поскольку int () не является lvalue, вы не можете присвоить int () .

Для определяемых пользователем типов присваивание является специальной функцией-членом, и функции-члены также могут вызываться для rvalue (см. §3.10, раздел 10). Вот почему A (). Operator = (a) правильно сформирован.

2
ответ дан 29 November 2019 в 21:49
поделиться

Из Выполняет ли C ++ инициализацию значения POD typedef? , который цитирует Стандарт:

Выражение T (), где T - спецификатор простого типа ( 7.1.5.2) для типа объекта без массива или типа void (возможно cv-квалифицированный) создает rvalue указанного типа , который инициализируется значением

Следовательно, int () является rvalue и не может быть присвоено, как вы видели в первом случае.

A () не будет спецификатором простого типа, и поэтому A () дает lvalue

1
ответ дан 29 November 2019 в 21:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: