PODs, non-PODs, rvalue и lvalues - PullRequest
       64

PODs, non-PODs, rvalue и lvalues

21 голосов
/ 19 февраля 2010

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


struct A {};

int main()
{
  int i;
  A a;

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

  return 0;
}

Ответы [ 3 ]

19 голосов
/ 19 февраля 2010

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

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

Пользовательские типы (UDT), однако, немного особенные: выможет преобразовать любое значение r в значение 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 явно запрещено какВы обнаружили, потому что у int нет оператора = реализованного таким же образом.Тем не менее, это небольшое несоответствие между типами на практике не имеет значения (по крайней мере, не так, как я обнаружил).


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

2 голосов
/ 19 февраля 2010

В моем понимании и int (), и A () должны быть значениями, нет?

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

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

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

1 голос
/ 19 февраля 2010

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

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

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

A () не будет указателем типа simlle, и, следовательно, A () возвращает lvalue

...