Подтвердить что ты не робот

Почему T() = T() разрешено?

Я считаю, что выражение T() создает rvalue (по стандарту). Однако следующий код компилируется (по крайней мере, на gcc4.0):

class T {};

int main()
{
    T() = T();
}

Я знаю, что это технически возможно, потому что функции-члены могут быть вызваны во временных рядах, а выше просто вызывает оператор = на временное значение rvalue, созданное с первого T().

Но концептуально это похоже на присвоение нового значения rvalue. Есть ли веская причина, почему это разрешено?

Изменить: причина, по которой я нахожу этот нечетный, строго запрещена для встроенных типов, которые все же разрешены для пользовательских типов. Например, int(2) = int(3) не будет компилироваться, потому что это "недопустимое значение lvalue в присваивании".

Итак, я думаю, что реальный вопрос в том, было ли это несколько непоследовательное поведение, встроенное в язык по какой-то причине? Или это по какой-то исторической причине? (Например, было бы концептуально более разумным, чтобы разрешать только функции-члены-члены в выражениях rvalue, но это невозможно сделать, поскольку это может сломать некоторый существующий код.)

4b9b3361

Ответ 1

Вот почему несколько классов в стандартной библиотеке могут быть реализованы. Рассмотрим, например, 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[], может получить доступ к битам, которые пространственно эффективно упакованы в целые числа.

Ответ 2

Это разрешено исключительно из-за перегрузки оператора и возможности перегрузить operator =, чтобы сделать что-то более интересное, например, распечатать на консоль или заблокировать мьютекс или что-то действительно.

Ответ 3

Да, вы назначаете новое значение rvalue. Точнее, вы вызываете функцию члена operator = на rvalue. Поскольку вы не используете встроенный оператор присваивания, почему вы думаете, что это должно быть проблемой? operator = - это функция-член класса, которая в большинстве своем похожа на любую другую функцию-член класса, включая тот факт, что ее можно вызывать на r-значениях.

Вероятно, вы также должны учитывать тот факт, что "быть rvalue" является свойством выражения, а не атрибутом объекта. Верно, что выражение T() оценивает значение r. Тем не менее, временный объект, выражаемый выражением T(), по-прежнему является объектом, к которому можно получить доступ как значение lvalue. Например, некоторые другие функции-члены могут быть вызваны по результату назначения, и он увидит "новое" (только что присвоенное) значение временного объекта через *this lvalue

(T() = T()).some_member_function();

Вы также можете продлить время жизни временного объекта, привязав к нему ссылку-const const T& r = T() = T();, а значение, видимое через r, будет "новым" значением объекта. Йоханнес правильно отметил в своем комментарии, это не будет привязано к временному.

Ответ 4

Вы можете ограничить оператор = работать только с lvalues ​​в С++ 0x:

class T
{
public:
    T& operator=(const T&) & = default;
};

Ответ 5

Из одного POV это противоречиво, но вы не замечаете, насколько он последователен: 1) ints и другие встроенные типы по-прежнему ведут себя так же, как и в C, 2) operator = на типах классов ведет себя как любой другой метод не требует еще одного особого случая.

Совместимость C была высоко оценена с начала С++, а С++, возможно, не будет здесь сегодня без нее. Так что эта часть вообще хорошая вещь.

Вторая точка занижена. Не специальный оператор обсадной колонны = позволяет бессмысленному коду "работать", но почему мы заботимся о бессмысленном коде в первую очередь? Мусор, мусор. Существующие правила дают ему определенное значение (UB здесь будет плохо) с незначительной стоимостью, насколько я когда-либо видел.

Учитывая мои барабанщики, все будет еще более упрощено, поэтому int() = int() будет разрешено. С++ 0x начинает двигаться в этом направлении с помощью rvalue-reference, prvalues ​​и т.д.