Правила программирования на С и С++. Главы 7-8
Страница 61. Перегруженные операции должны работать точно так же, как они работают в С


 

148. Перегруженные операции должны работать точно так же, как они работают в С.

Главной новой проблемой здесь являются адресные типы lvalue и rvalue. Выражения типа lvalue легко описываются в терминах С++: они являются просто ссылками. Компилятор С, вычисляя выражение, выполняет операции по одной за раз в порядке, определяемом правилами сочетательности и старшинства операций. Каждый этап в вычислениях использует временную переменную, полученную при предыдущей операции. Некоторые операции генерируют "rvalue" - действительные объекты, на самом деле содержащие значение. Другие операции создают "lvalue" - ссылки на объекты. (Кстати, "l" и "r" используются потому, что в выражении l=r слева от = генерируется тип lvalue. Справа образуется тип rvalue).

Вы можете сократить эффект неожиданности для своего читателя, заставив свои перегруженные операции-функции работать тождественно их эквивалентам на С в пределах того, что они могут. Далее описано, как работают операции С и как имитировать их поведение:

  • Операции присваивания (=, +=, -= и т.д.) и операции автоинкремента и автодекремента (++, --) требуют операндов типа lvalue для адресата - части, которая изменяется. Представьте ++ как эквивалент для +=1, чтобы понять, почему эта операция в той же категории, что и присваивание.
В перегруженных операциях функций-членов указатель this на самом деле является lvalue, поэтому здесь не о чем беспокоиться. На глобальном уровне левый операнд перегруженной бинарной операции присваивания (и единственный операнд перегруженной унарной операции присваивания) должен быть ссылкой.
  • Все другие операции могут иметь операнды как типа lvalue, так и rvalue.
Используйте ссылку на объект типа const для всех операндов. (Вы могли бы передавать операторы по значению, но обычно это менее эффективно).
  • Имена переменных составного типа (массивов) создают типы rvalue - временные переменные типа указателя на первый элемент, после инициализации на него и указывающие. Заметьте, что неверно представление о том, что вы не можете инкрементировать имя массива из-за того, что оно является константой. Вы не можете инкрементировать имя массива, потому что оно имеет тип rvalue, а все операции инкремента требуют операндов типа lvalue.
  • Имена переменных несоставного типа дают lvalue.
  • Операции *, -> и [] генерируют lvalue, когда относятся к несоставной переменной, иначе они работают подобно именам составных переменных. Если y не является массивом, то x->y создает тип lvalue, который ссылается на этого поле данных. Если y - массив, то x->y генерирует тип rvalue, который ссылается на первую ячейку этого массива.
В С++ перегруженные * и [] должны возвращать ссылки на указанный объект. Операция operator-> таинственна. Правила по существу заставляют вас использовать ее таким же образом, как вы делали бы это в С. Операция -> рассматривается как унарная с операндом слева от нее. Перегруженная функция должна возвращать указатель на что-нибудь, имеющее поля -, структуру класс или объединение. Компилятор будет затем использовать такое поле для получения lvalue или rvalue. Вы не можете перегрузить .(точку).
  • Все другие операнды генерируют тип rvalue.
Эквивалентные перегруженные операции должны возвращать объекты, а не ссылки или указатели.

 
« Предыдущая статья   Следующая статья »