Кто-либо мог объяснить детали с точки зрения rvalues, lvalues, ПЕРЕХОДНЫХ ПРИСТАВОК и непереходных приставок причина, почему первое выражение, отмеченное ниже, не в порядке, в то время как второе выражение, отмеченное ниже, в порядке? В моем понимании обоих интервалов () и () должен быть rvalues, нет?
struct A {};
int main()
{
int i;
A a;
int() = i; //Not OK (error).
A() = a; //OK.
return 0;
}
Значения 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 не меняет ничего из вышеперечисленного и действительно является отдельной проблемой.
Насколько я понимаю, как int (), так и A () должны быть r-значениями, не так ли?
Верно, epxression T ()
всегда является r-значением для скалярных и определяемых пользователем типов T
. Пока не используется const
, выражение T ()
является изменяемым r-значением , если быть более точным.
Присваивание, включающее скалярные типы, требует изменяемого lvalue в левой части оператора присваивания. Поскольку int ()
не является lvalue, вы не можете присвоить int ()
.
Для определяемых пользователем типов присваивание является специальной функцией-членом, и функции-члены также могут вызываться для rvalue (см. §3.10, раздел 10). Вот почему A (). Operator = (a)
правильно сформирован.
Из Выполняет ли C ++ инициализацию значения POD typedef? , который цитирует Стандарт:
Выражение T (), где T - спецификатор простого типа ( 7.1.5.2) для типа объекта без массива или типа void (возможно cv-квалифицированный) создает rvalue указанного типа , который инициализируется значением
Следовательно, int () является rvalue и не может быть присвоено, как вы видели в первом случае.
A () не будет спецификатором простого типа, и поэтому A () дает lvalue