POD, не-POD, rvalue и lvalues

Может ли кто-нибудь объяснить детали в терминах rvalues, lvalues, POD и non-POD, почему первое выражение, помеченное ниже, не ok, в то время как второе выражение, помеченное ниже, в порядке? В моем понимании, как int(), так и A() должны быть rvalues, нет?


struct A {};

int main()
{
  int i;
  A a;

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

  return 0;
}

Ответы

Ответ 1

Rvalues ​​- это то, что вы получаете из выражений (полезное упрощение, взятое из стандарта C, но не сформулированное на С++ standardese). Lvalues ​​- это "значения локатора". Lvalues ​​можно использовать как rvalues. Ссылки всегда lvalues, даже если const.

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

Пользовательские типы (UDT), однако, немного особенные: вы можете преобразовать любое значение rval в 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, кроме как через оператор присваивания, поставляемый компилятором, чтобы превратить значение r A() в *this. Чтобы процитировать стандарт, 12.8/10:

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

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

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

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


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

Ответ 2

В моем понимании, как int(), так и A() должны быть rvalues, no?

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

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

Для пользовательских типов назначение - это специальная функция-член, а функции-члены также могут быть вызваны на r-значениях (см. п. 10.10 раздела 10). Вот почему A().operator=(a) хорошо сформирован.

Ответ 3

Из Имеет ли С++ значение инициализации POD typedef?, которое цитирует стандарт:

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

Поэтому int() является r-значением и не может быть назначено, как вы видели в первом случае.

A() не будет параметром типа simlle-type, и, таким образом, A() даст lvalue