Почему T () = T () разрешено? - PullRequest
18 голосов
/ 28 мая 2010

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

class T {};

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

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

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

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

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

Ответы [ 5 ]

13 голосов
/ 28 мая 2010

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

7 голосов
/ 28 мая 2010

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

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

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

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

5 голосов
/ 28 мая 2010

Вы можете ограничить использование оператора = только для значений l в С ++ 0x:

class T
{
public:
    T& operator=(const T&) & = default;
};
4 голосов
/ 28 мая 2010

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

4 голосов
/ 28 мая 2010

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

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

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

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

...