26. Сохраняйте естественную семантику перегруженных операторов
26. Сохраняйте естественную семантику перегруженных операторов
Резюме
Программисты ненавидят сюрпризы. Перегружайте операторы только в случае веских на то оснований, и сохраняйте при этом их естественную семантику. Если это оказывается сложным, возможно, вы неверно используете перегрузку операторов.
Обсуждение
Хотя все (как мы надеемся) согласны с тем, что не следует реализовывать вычитание как оператор operator+, прочие ситуации не столь очевидны. Например, означает ли оператор operator* вашего класса Tensor скалярное или векторное умножение? Должен ли оператор operator+=(Tensor& t, unsigned u) прибавлять u к каждому из элементов t, или должен изменять размер t? В таких неоднозначных или не поддающихся интуитивному пониманию случаях следует использовать именованные функции, а не прибегать к шифрованию.
Для типов-значений (но не для всех типов; см. рекомендацию 32) следует придерживаться правила: "Если не знаешь, как поступить — поступай так, как int" [Meyers96]. Подражание поведению операторов встроенных типов и взаимоотношениям между ними гарантирует, что вы никого не приведете в замешательство. Если выбранная вами семантика заставляет кого-то удивленно поднять брови, может быть, перегрузка оператора — не самая лучшая идея?
Программисты ожидают, что операторы идут в связке — если выражение a@b имеет определенный смысл для некоторого определенного вами оператора @ (возможно, после преобразования типов), то задайте сами себе вопрос: можно ли написать b@a без неприятных последствий? Можно ли написать a@=b? (См. рекомендацию 27.) Если оператор имеет обратный оператор (например, + и -, * и /), то поддерживаются ли они оба?
От именованных функций не ожидается наличие соответствующих взаимоотношений, так что для большей ясности кода, если возможно неверное истолкование семантики перегруженных операторов, лучше использовать именно функции.
Исключения
Имеются высокоспециализированные библиотеки (например, генераторы синтаксических анализаторов), в предметной области которых соглашения о семантике операторов существенно отличаются от их значений в С++ (например, при работе с регулярными выражениями оператор operator* может использоваться для выражения "ноль или большее количество"). Предпочтительно найти альтернативу необычной перегрузке оператора (например, в регулярных выражениях [C++TR104] используются строки, так что * может использоваться естественным образом, без перегрузки операторов). Если все же после тщательных размышлений вы решили использовать операторы, убедитесь, что вы четко определили единую схему для всех ваших соглашений, и при этом не затеяли опасные игры со встроенными операторами.
Ссылки
[Cline99] §23.02-06 • [C++TR104] §7 • [Dewhurst03] §85-86 • [Koenig97] §4 • [Lakos96] §9.1.1 • [Meyers96] §6 • [Stroustrup00] §11.1 • [Sutter00] §41