Я не понимаю аргументы против перегрузки операторов [закрыто]

82

Я только что прочитал одну из статей Джоэла, в которых он говорит:

В общем, я должен признать, что я немного боюсь языковых особенностей, которые скрывают вещи . Когда вы видите код

i = j * 5;

… В C вы знаете, по крайней мере, что j умножается на пять, а результаты сохраняются в i.

Но если вы видите тот же фрагмент кода на C ++, вы ничего не знаете. Ничего. Единственный способ узнать, что в действительности происходит в C ++, - это выяснить, что такое типы i и j, что может быть объявлено где-то еще. Это потому, что j может иметь тип, который operator*перегружен, и он делает что-то очень остроумное, когда вы пытаетесь его умножить.

(Акцент мой.) Боишься языковых особенностей, которые скрывают вещи? Как ты можешь бояться этого? Разве сокрытие вещей (также известное как абстракция ) не является одной из ключевых идей объектно-ориентированного программирования? Каждый раз, когда вы вызываете метод a.foo(b), вы не представляете, что это может сделать. Вы должны выяснить, что это за типы aи bчто, что может быть объявлено где-то еще. Так стоит ли нам отказываться от объектно-ориентированного программирования, потому что оно слишком много скрывает от программиста?

И чем это j * 5отличается от того j.multiply(5), что вам, возможно, придется написать на языке, который не поддерживает перегрузку операторов? Опять же, вам придется выяснить тип jи заглянуть внутрь multiplyметода, потому что вот, jможет быть, это тот тип, у которого есть multiplyметод, который делает что-то ужасно остроумное.

«Муахаха, я злой программист, который называет метод multiply, но на самом деле он абсолютно неясен и не интуитивен, и ему абсолютно нечего делать с умножением». Это сценарий, который мы должны учитывать при разработке языка программирования? Тогда мы должны отказаться от идентификаторов из языков программирования на том основании, что они могут вводить в заблуждение!

Если вы хотите узнать, что делает метод, вы можете взглянуть на документацию или заглянуть внутрь реализации. Перегрузка операторов - просто синтаксический сахар, и я не вижу, как это вообще меняет игру.

Пожалуйста, просветите меня.

fredoverflow
источник
20
+1: Хорошо написано, хорошо аргументировано, интересная тема и весьма дискуссионная. Яркий пример вопроса.
Аллон Гуралнек
19
+1: Люди слушают Джоэла Спольски, потому что он хорошо пишет и хорошо известен. Но это не делает его правильным 100% времени. Я согласен с вашим аргументом. Если бы мы все следовали логике Джоэла, мы бы никуда не попали.
Никто
5
Я бы сказал, что либо i, либо j объявляются локально, так что вы можете быстро увидеть их тип, либо они являются пустыми именами переменных и должны быть соответствующим образом переименованы.
Кэмерон Макфарланд
5
+1, но не забывайте лучшую часть статьи Джоэла: после пробежки марафона в направлении правильного ответа он без видимой причины останавливается на расстоянии 50 футов от него. Неправильный код не должен просто выглядеть неправильно; это не должно компилироваться.
Ларри Коулман
3
@Larry: Вы можете сделать так, чтобы неправильный код не скомпилировался, определив классы соответствующим образом, поэтому в его примере у вас могут быть SafeString и UnsafeString в C ++ или RowIndex и ColumnIndex, но тогда вам придется использовать перегрузку операторов, чтобы заставить их вести себя интуитивно.
Дэвид Торнли

Ответы:

32

Абстракция «скрывает» код, так что вам не нужно беспокоиться о внутренней работе, а часто и о том, что вы не можете их изменить, но целью было не помешать вам взглянуть на него. Мы просто делаем предположения об операторах, и, как сказал Джоэл, это может быть где угодно. Наличие функции программирования, требующей установки всех перегруженных операторов в определенном месте, может помочь найти ее, но я не уверен, что это облегчает ее использование.

Я не вижу, чтобы заставить * делать то, что не очень похоже на умножение, лучше, чем функция Get_Some_Data, которая удаляет данные.

JeffO
источник
13
+1 За бит «Я не вижу». Языковые функции предназначены для использования, а не для злоупотреблений.
Майкл К
5
Тем не менее, у нас есть <<оператор, определенный для потоков, который не имеет ничего общего с битовым сдвигом, прямо в стандартной библиотеке C ++.
Малкольм
оператор «побитового сдвига» вызывается только по историческим причинам. При применении к стандартным типам он выполняет побитовый сдвиг (так же, как оператор + складывает числа вместе при применении к числовым типам), однако при применении к сложному типу он может делать то, что ему нравится, до тех пор, пока он делает смысл для этого типа.
gbjbaanb
1
* также используется для разыменования (как это делается умными указателями и итераторами); непонятно, где поставить границу между хорошей и плохой перегрузкой
мартинкунев
Это не было бы просто где-нибудь, это было бы в определении типа j.
Энди
19

ИМХО, языковые функции, такие как перегрузка операторов, дают программисту больше возможностей. И, как мы все знаем, с большой силой приходит большая ответственность. Функции, которые дают вам больше возможностей, также дают вам больше возможностей стрелять себе в ногу, и, очевидно, должны использоваться разумно.

Например, это имеет смысл перегружать +или *оператор для class Matrixили class Complex. Каждый сразу узнает, что это значит. С другой стороны, тот факт, что это +означает, что конкатенация строк не совсем очевидна, хотя Java делает это как часть языка, а STL делает это с std::stringиспользованием перегрузки операторов.

Еще один хороший пример того, когда перегрузка операторов делает код более понятным, - это умные указатели в C ++. Вы хотите, чтобы умные указатели вели себя как обычные указатели в максимально возможной степени, поэтому имеет смысл перегрузить унарные *и ->операторы.

По сути, перегрузка операторов - это не что иное, как еще один способ назвать функцию. И есть правило для именования функций: имя должно быть описательным, чтобы сразу было понятно, что делает функция. Точно такое же правило применяется к перегрузке операторов.

Дима
источник
1
Ваши последние два предложения доходят до сути возражения против перегрузки операторов: желание, чтобы весь код был сразу очевидным.
Ларри Колман
2
Разве не очевидно, что означает M * N, где M и N имеют тип Matrix?
Дима
2
@ Фред: Нет. Существует один вид умножения матриц. Вы можете умножить матрицу mxn на матрицу nxk и получить матрицу mxk.
Дима
1
@FredOverflow: Существуют различные способы умножения трехмерного вектора: один дает вам скаляр, а другой - другой трехмерный вектор, поэтому перегрузка *для них может вызвать путаницу. Возможно, вы могли бы использовать operator*()для точечного продукта и operator%()для перекрестного продукта, но я бы не стал делать это для библиотеки общего пользования.
Дэвид Торнли
2
@Martin Beckett: Нет C ++ , не разрешается изменять порядок , A-Bкак B-Aлибо, и все операторы следуют этому шаблону. Хотя всегда есть одно исключение: когда компилятор может доказать, что это не имеет значения, ему разрешается все переставлять.
Sjoerd
9

В Haskell «+», «-», «*», «/» и т. Д. Являются просто (инфиксными) функциями.

Должны ли вы назвать инфиксную функцию «плюс», как в «4 плюс 2»? Почему нет, если дополнение - это то, что делает ваша функция. Должны ли вы назвать вашу «плюс» функцию «+»? Почему нет.

Я думаю, что проблема с так называемыми «операторами» заключается в том, что они в основном напоминают математические операции, и существует не так много способов их интерпретировать, и поэтому существуют большие надежды на то, что делает такой метод / функция / оператор.

РЕДАКТИРОВАТЬ: сделал мою точку зрения более ясным

LennyProgrammers
источник
Хм, кроме того, что унаследовано от C, C ++ (и это то, о чем спрашивал Фред) делает почти то же самое. Что вы думаете о том, хорошо это или плохо?
2010 года
@sbi Я люблю перегружать оператор ... На самом деле , даже C имеет перегруженные операторы ... Вы можете использовать их для int, float, long longи что угодно. Так о чем это все?
FUZxxl
@FUZxxl: Это все о пользовательских операторах, перегружающих встроенные.
ВОО
1
В @sbi Haskell нет различий между встроенным и определяемым пользователем . Все операторы равны. Вы даже можете включить некоторые расширения, которые удаляют все предопределенные вещи и позволяют писать что угодно с нуля, включая любые операторы.
FUZxxl
@FUZxxl: Это вполне может быть, но те, кто противостоит перегруженным операторам, обычно не возражают против использования встроенных +для различных встроенных типов чисел, а создают перегрузки, определяемые пользователем. отсюда мой комментарий.
ВОО
7

Основываясь на других ответах, которые я видел, я могу только заключить, что реальное возражение против перегрузки операторов - это желание сразу же очевидного кода.

Это трагично по двум причинам:

  1. Достигнув логического завершения, принцип, что код должен быть сразу очевидным, заставил бы всех нас писать код на языке COBOL.
  2. Вы не учитесь на коде, который сразу очевиден. Вы учитесь на коде, который имеет смысл, если вам потребуется время, чтобы подумать о том, как он работает.
Ларри Коулман
источник
Изучение кода не всегда является основной целью. В случае, когда «функция X дает сбой, человек, который написал ее, ушел из компании, и вы должны исправить это как можно скорее», я бы предпочел, чтобы код был сразу очевиден.
Errorsatz
5

Я несколько согласен.

Если вы пишете multiply(j,5), jможет быть скалярного или матричного типа, что делает multiply()более или менее сложным, в зависимости от того, что jесть. Однако, если вы полностью откажетесь от идеи перегрузки, тогда нужно будет назвать функцию multiply_scalar()или multiply_matrix()сделать ее очевидной, что происходит под ней.

Есть код, где многие из нас предпочитают его одним способом, и есть код, где большинство из нас предпочли бы его другим способом. Однако большая часть кода попадает в середину между этими двумя крайностями. То, что вы предпочитаете, зависит от вашего опыта и личных предпочтений.

SBI
источник
Хорошая точка зрения. Тем не менее, отказ от перегрузки вообще не очень подходит для общего программирования ...
fredoverflow
@FredO: Конечно нет. Но универсальное программирование - это использование одного и того же алгоритма для очень разных типов, поэтому предпочитающим multiply_matrix()не понравится и универсальное программирование.
2010 года
2
Вы довольно оптимистичны в отношении имен, не так ли? Исходя из некоторых мест, где я работал, я ожидал бы такие имена, как 'multiply () `и' multiplym ()` или, может быть, real_multiply()или около того. Разработчики часто плохо разбираются в именах и, operator*()по крайней мере, будут последовательны.
Дэвид Торнли
@ Дэвид: Да, я пропустил тот факт, что имена могут быть плохими. Но тогда мы могли бы также предположить, что operator*()может сделать что-то глупое, jэто макрос, вычисляющий выражения, включающие пять вызовов функций, и еще много чего. Тогда вы больше не можете сравнивать эти два подхода. Но да, называть вещи хорошо сложно, хотя и стоит того времени, которое требуется.
2010 года
5
@ Дэвид: И поскольку называть вещи сложно, имена должны быть изгнаны из языков программирования, верно? Просто слишком легко ошибиться! ;-)
fredoverflow
4

Я вижу две проблемы с перегрузкой оператора.

  1. Перегрузка изменяет семантику оператора, даже если это не предусмотрено программистом. Например, при перегрузке &&, ||или ,вы потеряете очки последовательности , которые вытекают из встроенных вариантов этих операторов (а также поведение короткого замыкания логических операторов). По этой причине лучше не перегружать эти операторы, даже если язык это позволяет.
  2. Некоторые люди считают перегрузку операторов такой замечательной функцией, что начинают использовать ее везде, даже если это не подходящее решение. Это заставляет других людей чрезмерно реагировать в другом направлении и предостерегать от использования перегрузки оператора. Я не согласен ни с одной из групп, но прихожу на второй план: перегрузка оператора должна использоваться экономно и только тогда, когда
    • перегруженный оператор имеет естественное значение как для экспертов в области, так и для экспертов по программному обеспечению. Если эти две группы не согласны с естественным значением оператора, не перегружайте его.
    • для вовлеченного типа (типов) нет естественного значения для оператора, и непосредственный контекст (предпочтительно одно и то же выражение, но не более нескольких строк) всегда проясняет, что означает оператор. Примером этой категории будет operator<<для потоков.
Барт ван Инген Шенау
источник
1
+1 от меня, но второй аргумент в равной степени может быть применен к наследованию. Многие люди не имеют понятия о наследовании и пытаются применить его ко всему. Я думаю, что большинство программистов согласятся с тем, что возможно злоупотреблять наследованием. Означает ли это, что наследование является «злом» и должно быть исключено из языков программирования? Или мы должны оставить это, потому что это также может быть полезным?
fredoverflow
@FredOverflow Второй аргумент может быть применен ко всему, что является «новым и горячим». Я привожу это не в качестве аргумента для удаления перегрузки операторов из языка, а в качестве причины, по которой люди выступают против этого. Насколько мне известно, перегрузка оператора полезна, но ее следует использовать с осторожностью.
Барт ван Инген Шенау
ИМХО, допускать перегрузки &&и ||таким образом, который не подразумевает секвенирование, было большой ошибкой (ИМХО, если бы C ++ собирался разрешить их перегрузку, он должен был бы использовать специальный формат «двух функций», при этом требовалась первая функция чтобы вернуть тип, который был неявно конвертируемым в целое число; вторая функция могла бы принимать два или три аргумента, причем «дополнительный» аргумент второй функции был бы типом возврата первого. Компилятор вызывал бы первую функцию и затем, если он вернул ненулевое значение, оцените второй операнд и вызовите в нем вторую функцию.)
суперкат
Конечно, это не так странно, как возможность перегружать оператор запятой. Между прочим, одна вещь с перегрузкой, которую я на самом деле не видел, но хотел бы, была бы средством жесткой привязки доступа к члену, позволяющим foo.bar[3].Xобрабатывать выражения вроде fooкласса, а не требовать fooпредоставления члена, который может поддержать подписку и затем выставить участника X. Если кто-то хочет форсировать оценку через фактический доступ участника, он написал бы ((foo.bar)[3]).X.
суперкат
3

Исходя из моего личного опыта, способ Java, позволяющий использовать несколько методов, но не перегружать оператор, означает, что всякий раз, когда вы видите оператор, вы точно знаете , что он делает.

Вы не должны видеть, *вызывает ли странный код, но знаете, что это умножение, и оно ведет себя точно так же, как это определено в Спецификации языка Java. Это означает, что вы можете сконцентрироваться на реальном поведении, вместо того чтобы выяснить все, что задает программист.

Другими словами, запрещение перегрузки операторов является преимуществом для читателя , а не автора , и, следовательно, облегчает обслуживание программ!


источник
+1, с оговоркой: C ++ дает вам достаточно веревки, чтобы повеситься. Но если я хочу реализовать связанный список в C ++, я хотел бы иметь возможность использовать [] для доступа к n-му элементу. Имеет смысл использовать операторы для данных, для которых (математически говоря) они действительны.
Майкл К
@ Майкл, ты не можешь жить с list.get(n)синтаксисом?
@ Thorbjørn: На самом деле это тоже хорошо, возможно, плохой пример. Время может быть лучше - перегрузка +, - будет иметь смысл, а не time.add (anotherTime).
Майкл К
4
@Michael: О связанных списках, std::listне перегружает operator[](и не предоставляет никаких других средств индексирования в списке), потому что такой операцией будет O (n), и интерфейс списка не должен предоставлять такую ​​функцию, если вы заботитесь об эффективности. У клиентов может возникнуть соблазн перебирать связанные списки с индексами, что делает ненужными алгоритмы O (n) O (n ^ 2). Вы видите это довольно часто в коде Java, особенно если люди работают с Listинтерфейсом, целью которого является полное абстрагирование сложности.
fredoverflow
5
@Thor: «Но чтобы быть уверенным, нужно проверить :)» ... Опять же, это не связано с перегрузкой операторов . Если вы видите time.add(anotherTime), вам также придется проверить, правильно ли программист библиотеки реализовал операцию добавления (что бы это ни значило).
fredoverflow
3

Одно из различий между перегрузкой a * bи вызовом multiply(a,b)состоит в том, что последний может быть легко найден. Если multiplyфункция не перегружена для разных типов, вы можете точно узнать, что будет делать функция, без необходимости отслеживать типы aи b.

У Линуса Торвальдса есть интересный аргумент о перегрузке операторов. При разработке ядра Linux, когда большая часть изменений отправляется через патчи по электронной почте, важно, чтобы сопровождающие могли понять, что будет делать патч с несколькими строками контекста вокруг каждого изменения. Если функции и операторы не перегружены, то патч легче прочитать независимо от контекста, поскольку вам не нужно просматривать измененный файл, выясняя, что представляют собой все типы, и проверять перегруженные операторы.

Скотт Уэльс
источник
Разве ядро ​​Linux не разработано в чистом C? Зачем вообще обсуждать (операторскую) перегрузку в этом контексте?
fredoverflow
Проблемы одинаковы для любого проекта с похожим процессом разработки, независимо от языка. Чрезмерная перегрузка может затруднить понимание влияния изменений, если вам нужно всего лишь несколько строк из файла исправления.
Скотт Уэльс
@FredOverflow: ядро ​​Linux действительно в GCC C. Он использует всевозможные расширения, которые иногда дают его C почти C ++. Я думаю о некоторых необычных манипуляциях.
Зан Рысь
2
@ Скотт: Нет смысла обсуждать «злобность» перегрузки в отношении проектов, запрограммированных в C, потому что C не имеет возможности перегружать функции.
fredoverflow
3
Мне кажется, у Линуса Торвальдса узкая точка зрения. Иногда он критикует вещи, которые не очень полезны для программирования ядра Linux, как будто это делает их непригодными для общего использования. Subversion является одним из примеров. Это хорошая VCS, но для разработки ядра Linux действительно нужна распределенная VCS, поэтому Линус критиковал SVN в целом.
Дэвид Торнли
2

Я подозреваю, что это как-то связано с нарушением ожиданий. Если вы привыкли к C ++, вы привыкли к тому, что поведение оператора не полностью определяется языком, и вы не удивитесь, когда оператор сделает что-то странное. Если вы привыкли к языкам, которые не имеют такой возможности, а затем видите код C ++, вы несете ожидания от этих других языков и можете быть противно удивлены, когда обнаружите, что перегруженный оператор делает что-то странное.

Лично я думаю, что есть разница. Когда вы можете изменить поведение встроенного синтаксиса языка, он становится более непрозрачным для размышлений. Языки, которые не допускают метапрограммирование, синтаксически менее мощны, но концептуально проще для понимания.

Джори Себрехтс
источник
Перегруженные операторы никогда не должны делать «что-то странное». Хорошо, если он делает что-то сложное, конечно. Но перегрузка только тогда, когда она имеет одно, очевидное значение.
Sjoerd
2

Я думаю, что перегрузка математических операторов не является реальной проблемой с перегрузкой операторов в C ++. Я думаю, что перегрузка операторов, которые не должны полагаться на контекст выражения (то есть типа), является «злой». Например, перегруз , [ ] ( ) -> ->* new deleteили даже одинарный *. У вас есть определенный набор ожиданий от тех операторов, которые никогда не должны меняться.

Аллон Гуралнек
источник
+1 Не делайте [] эквивалентом ++.
Майкл К
3
Вы говорите , что мы не должны быть в состоянии перегружать операторы , указанные на всех ? Или вы просто говорите, что мы должны перегружать их только для вменяемых целей? Потому что я не хотел бы видеть контейнеры без operator[], функторы без operator(), умные указатели без operator->и так далее.
fredoverflow
Я говорю, что потенциальная проблема перегрузки операторов математическими операциями мала по сравнению с этими операторами. Делать что-то умное или сумасшедшее с математическими операторами может быть затруднительно, но перечисленные мною операторы, о которых люди обычно думают не как о операторах, а скорее как о базовых элементах языка, всегда должны соответствовать ожиданиям, определенным языком. []всегда должен быть аксессором, подобным массиву, и ->всегда должен означать доступ к члену. Неважно, массив это или другой контейнер, умный указатель или нет.
Аллон Гуралнек
2

Я прекрасно понимаю, что вам не нравится аргумент Джоэла о сокрытии. И я нет. Действительно, гораздо лучше использовать «+» для таких вещей, как встроенные числовые типы или для ваших собственных, например, для матрицы. Я признаю, что это аккуратно и элегантно, чтобы иметь возможность умножать две матрицы на «*» вместо «.multiply ()». В конце концов, у нас одинаковая абстракция в обоих случаях.

Здесь болит читаемость вашего кода. В реальных случаях, а не в академическом примере умножения матриц. Особенно, если ваш язык позволяет, например, определять операторы, которые изначально не присутствуют в ядре языка =:=. На этом этапе возникает множество дополнительных вопросов. О чем этот чертов оператор? Я имею в виду, каков приоритет этой вещи? Что такое ассоциативность? В каком порядке a =:= b =:= cдействительно выполняется?

Это уже аргумент против перегрузки операторов. Все еще не убежден? Проверка правил приоритета заняла у вас не более 10 секунд? Хорошо, пойдем дальше.

Если вы начнете использовать язык, который допускает перегрузку операторов, например тот популярный, чье имя начинается с 'S', вы быстро поймете, что разработчики библиотек любят переопределять операторы. Конечно, они хорошо образованы, они следуют лучшим практикам (здесь нет цинизма), и все их API имеют смысл, когда мы смотрим на них отдельно.

Теперь представьте, что вам нужно использовать несколько API, которые интенсивно используют операторы, перегружающие друг друга в одном куске кода. Или даже лучше - вы должны прочитать какой-то устаревший код, подобный этому. Это когда перегрузка оператора действительно отстой. В основном, если в одном месте много перегруженных операторов, они скоро начнут смешиваться с другими не буквенно-цифровыми символами в коде вашей программы. Они будут смешиваться с не буквенно-цифровыми символами, которые на самом деле не являются операторами, а скорее являются более фундаментальными элементами грамматики языка, которые определяют такие вещи, как блоки и области действия, операторы управления потоком формы или обозначают некоторые мета-вещи. Вам нужно будет надеть очки и подвести глаза на 10 см ближе к ЖК-дисплею, чтобы понять, что такое визуальный беспорядок.

akosicki
источник
1
Перегрузка существующих операторов и придумывание новых операторов - это не одно и то же, но +1 от меня.
fredoverflow
1

В общем, я избегаю использования перегрузки операторов неинтуитивным способом. То есть, если у меня есть числовой класс, перегрузка * допустима (и приветствуется). Однако, если бы у меня был класс Employee, что бы перегрузка * сделала? Другими словами, операторы перегрузки интуитивно понятны, что облегчает их чтение и понимание.

Приемлемый / Воодушевленные:

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

Недопустимо:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};
Зак Хоуленд
источник
1
Умножать сотрудников? Конечно, это оскорбительное преступление, если они делают это на столе в зале заседаний, то есть.
gbjbaanb
1

В дополнение к тому, что уже было сказано, есть еще один аргумент против перегрузки операторов. Действительно, если вы пишете +, это очевидно, что вы имеете в виду добавление чего-либо к чему-либо. Но это не всегда так.

Сам C ++ является отличным примером такого случая. Как stream << 1предполагается читать? поток сдвинут влево на 1? Это совсем не очевидно, если вы явно не знаете, что << в C ++ также пишет в поток. Однако, если бы эта операция была реализована как метод, ни один здравомыслящий разработчик не напишет o.leftShift(1), это было бы что-то вроде o.write(1).

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

Малькольм
источник
1

По сравнению с описанными методами, операторы короче, но они также не требуют скобок. Скобки относительно неудобны для ввода. И вы должны сбалансировать их. Всего для любого вызова метода требуется три символа простого шума по сравнению с оператором. Это делает использование операторов очень, очень заманчивым.
Зачем еще кому-то этого хотеть cout << "Hello world"?

Проблема с перегрузкой заключается в том, что большинство программистов невероятно ленивы, и большинство программистов не могут себе этого позволить.

То, что программистов на C ++ заставляет злоупотреблять перегрузкой операторов, заключается не в их наличии, а в отсутствии более совершенного способа выполнения вызовов методов. И люди не просто боятся перегрузки оператора, потому что это возможно, а потому, что это сделано.
Обратите внимание, что, например, в Ruby и Scala никто не боится перегрузки операторов. Помимо того факта, что использование операторов на самом деле не короче методов, другая причина заключается в том, что Ruby ограничивает перегрузку операторов разумным минимумом, в то время как Scala позволяет объявлять свои собственные операторы, таким образом избегая коллизий.

back2dos
источник
или, в C #, для использования + =, чтобы связать событие с делегатом. Я не думаю, что обвинять языковые особенности в глупости программиста - это конструктивный путь вперед.
gbjbaanb
0

Причина, по которой перегрузка оператора является пугающей, заключается в том, что существует большое количество программистов, которые никогда даже не ДУМАЮТ, что *не означает просто «умножение», в то время как такой метод, как foo.multiply(bar)минимум, сразу же указывает этому программисту, что кто-то написал собственный метод умножения , В этот момент они задаются вопросом, почему и идут расследование.

Я работал с «хорошими программистами», которые занимали должности высокого уровня, которые создавали бы методы, называемые «CompareValues», которые принимали бы 2 аргумента, применяли значения одного к другому и возвращали логическое значение. Или метод с именем «LoadTheValues», который отправляет в базу данных 3 других объекта, получает значения, выполняет вычисления, изменяет thisи сохраняет его в базе данных.

Если я работаю в команде с этими типами программистов, я сразу же узнаю, чтобы исследовать вещи, над которыми они работали. Если они перегружают оператора, я никак не могу знать, что они это сделали, кроме как предположить, что они сделали, и пойти искать.

В идеальном мире или команде с идеальными программистами перегрузка операторов, вероятно, фантастический инструмент. Однако мне еще предстоит работать в команде идеальных программистов, поэтому это страшно.

Джеймс П. Райт
источник