Почему T () = T () разрешено?
Я считаю, что выражение T()
создает rvalue (по стандарту). Однако следующий код компилируется (по крайней мере, на gcc4. 0):
class T {};
int main()
{
T() = T();
}
Я знаю, что технически это возможно, потому что функции-члены могут быть вызваны на temporaries, и выше просто вызывается оператор= на rvalue temporary, созданный из первого T()
.
Но концептуально это похоже на присвоение нового значения rvalue. Есть ли веская причина, почему это разрешено?
редактировать: Причина, по которой я нахожу это странным, заключается в том, что это строго запрещено для встроенных типов, но разрешено для пользовательских типов. Например, int(2) = int(3)
не будет компилироваться, потому что это "недопустимое значение lvalue в присваивании".
5 ответов:
Именно поэтому в стандартной библиотеке может быть реализовано несколько классов. Рассмотрим, например,
std::bitset<>::operator[]
// bit reference: class reference { friend class bitset; reference(); public: ˜reference(); reference& operator=(bool x); // for b[i] = x; reference& operator=(const reference&); // for b[i] = b[j]; bool operator˜() const; // flips the bit operator bool() const; // for x = b[i]; reference& flip(); // for b[i].flip(); }; reference operator[](size_t pos); // for b[i];
Если вы делаете
bits[i] = true
, вы точно присваиваете некоторое значение rvalue типа класса. Прокси, возвращаемыйoperator[]
, может получить доступ к битам, которые являются пространством, эффективно упакованным в целые числа.
Это разрешено исключительно из-за перегрузки оператора и возможности того, что вы можете перегрузить
operator =
, чтобы сделать что-то более причудливое, например, распечатать консоль, или заблокировать мьютекс, или что-нибудь действительно.
Да, вы присваиваете новое значение rvalue. Точнее, вы вызываете функцию-член
operator =
на rvalue. Поскольку вы не используете встроенный оператор присваивания, почему вы думаете, что это должно быть проблемой?operator =
является функцией-членом класса, которая во многих отношениях аналогична любой другой функции-члену класса, включая тот факт, что она может быть вызвана на rvalues.Вероятно, вам также следует принять во внимание тот факт, что" быть rvalue " - это свойство выражения, а не свойства объекта. Верно, что выражение
T()
вычисляется как rvalue. Тем не менее, временный объект, производимый выражениемT()
, все еще является объектом , который также может быть доступен в качестве lvalue. Например, некоторая другая функция-член может быть вызвана в результате присваивания, и она будет видеть "новое" (свежеприведенное) значение временного объекта через*this
lvalue(T() = T()).some_member_function();
вы также можете продлить срок службы устройства. временным путем прикрепления к нему const-ссылкиconst T& r = T() = T();
и значение, видимое черезr
, будет "новым" значением объекта.Как правильно отметил Иоганн в своем комментарии, это не будет привязывать его к временному.
Вы можете ограничить оператор= работать только на lvalues в C++0x:
class T { public: T& operator=(const T&) & = default; };
С одной стороны, это непоследовательно, но вы упускаете из виду, как это является последовательным: 1) ints и другие встроенные типы все еще ведут себя так, как они делают в C, 2) operator= on class-types ведет себя так же, как и любой другой метод, не требуя еще одного специального случая.
Совместимость C высоко ценилась с самого начала C++, и C++, возможно, не был бы здесь сегодня без нее. Так что эта часть, как правило, хорошая вещь.Второй момент занижен. Не специальный кожух оператор= позволяет бессмысленному коду "работать", но почему мы заботимся о бессмысленном коде в первую очередь? Мусор внутрь, мусор наружу. Нынешние правила придают ему определенный смысл (UB здесь было бы плохо) с незначительной стоимостью, насколько я когда-либо видел.
Учитывая мои druthers, все будет упрощено еще больше, так что
int() = int()
будет разрешено. C++0x начинает двигаться в этом направлении с rvalue-ссылками, prvalues и т. д.