Модули, не-модули, rvalue и lvalues
Вопрос
Может ли кто-нибудь объяснить детали в терминах rvalues, lvalues, PODs и non-PODs причина, по которой первое выражение, отмеченное ниже, является не ок, в то время как второе выражение, отмеченное ниже, в порядке?В моем понимании и int(), и A() должны быть rvalues, не так ли?
struct A {};
int main()
{
int i;
A a;
int() = i; //Not OK (error).
A() = a; //OK.
return 0;
}
Решение
Значения R - это то, что вы получаете из выражений (полезное упрощение, взятое из стандарта C, но не сформулированное в стандартах C ++).Значения Lvalues - это "значения локатора".Значения Lvalues могут быть использованы как значения rvalues.Ссылки всегда являются значениями lvalues, даже если они const.
Основное различие, о котором вы должны знать, можно свести к одному пункту:вы не можете использовать адрес rvalue (опять же, не стандартный, но полезное обобщение правил).Или, другими словами, вы не можете зафиксировать точную Расположение для rvalue — если бы вы могли, то у вас был бы значение lvalue.(Однако вы можете привязать const& к 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()
в *this
.Цитируя стандарт, 12.8/10:
Если в определении класса явно не объявлен оператор присваивания копии, объявляется один неявно.Неявно объявленный оператор присваивания копии для класса X будет иметь вид
X& X::operator=(const X&)
А затем это продолжается с дополнительными квалификациями и спецификациями, но это здесь самое важное.Поскольку это функция-член, она может быть вызвана для rvalues , точно так же, как Special::get_lvalue может быть, как если бы вы написали A().operator=(a)
вместо того , чтобы A() = a
.
В int() = 1
явно запрещено, как вы обнаружили, потому что ints не имеют operator=, реализованного таким же образом.Однако это небольшое расхождение между типами на практике не имеет значения (по крайней мере, не то, что я обнаружил).
POD означает Обычные Старые данные и представляет собой набор требований, которые указывают, что использование memcpy эквивалентно копированию.Non-POD - это любой тип, для которого вы не можете использовать memcpy для копирования (естественная противоположность POD, здесь ничего скрытого), который, как правило, относится к большинству типов, которые вы напишете на C ++.Быть POD или не быть POD не меняет ничего из вышеперечисленного и на самом деле является отдельной проблемой.
Другие советы
В моем понимании и int(), и A() должны быть rvalues, не так ли?
Правильно, выражение epx T()
всегда является значением r для скалярных и определяемых пользователем типов T
.До тех пор, пока нет const
задействовано, выражение T()
является изменяемое значение rvalue, если быть более точным.
Присвоение, включающее скалярные типы, требует изменяемого значение lvalue в левой части оператора присваивания.С тех пор как int()
не является значением lvalue, вы не можете присвоить int()
.
Для пользовательских типов присваивание является специальной функцией-членом, и функции-члены также могут вызываться на значения rv (см. §3.10 раздел 10).Вот почему A().operator=(a)
хорошо сформирован.
От Выполняет ли C ++ инициализацию значения POD typedef?, который цитирует Стандарт:
Выражение T(), где T - это спецификатор простого типа (7.1.5.2) для типа объекта, не являющегося полным массивом, или (возможно, с указанием cv) типа void, создает rvalue указанного типа, который инициализируется значением
Следовательно, int() является rvalue и не может быть присвоен, как вы видели в вашем первом случае.
A() не был бы спецификатором simlle-type и, таким образом, A() выдает значение lvalue