Що робить оператора Scala перевантаження «хорошим», а C ++ - «поганим»?


155

Перевантаження оператора в C ++ багато хто вважає поганою річчю (tm), і помилкою не повторюватися в нових мовах. Звичайно, це була одна особливість, яка спеціально випала при розробці Java.

Тепер, коли я почав читати на Scala, я вважаю, що він дуже схожий на перевантаження оператора (хоча технічно він не має перевантаження оператора, оскільки він не має операторів, а лише функції). Однак це, здається, не буде якісно відрізнятися від перевантаження оператора в C ++, де, наскільки я пам'ятаю, оператори визначаються як спеціальні функції.

Отже, моє запитання полягає в тому, що робить ідею визначення "+" у Scala кращою ідеєю, ніж це було в C ++?


27
Ні C ++, ні Scala не були визначені загальним консенсусом серед усіх програмістів. Я не думаю, що існує протиріччя між тим, що деякі люди скупіться про C ++, і тим, що деякі люди не скупіться про Scala.
Стів Джессоп

16
Немає нічого поганого в перевантаженні оператора на C ++.
Щеня

5
Це не нове, окрім того, як я захищаю C ++ при перевантаженні оператора та інших "розширених" функцій, ставлять під сумнів простий: C ++ дає нам усі сили використовувати / зловживати ним, як вважаємо за потрібне. Мені завжди подобалося, як нас вважають компетентними та автономними та не потребують таких рішень, як прийняті для нас.
Елліотт

Scala був розроблений як десятиліття після c ++. Виявляється, людина, що стоїть за нею, є надзвичайно дивакуватою в мовах програмування. Нічого поганого як такого, якщо дотримуватися c ++ або Scala ще протягом 100 років, стає зрозуміло, що, ймовірно, обидва погані! Бути упередженим, мабуть, є нашою природою, але ми можемо боротися з цим, просто подивіться на історію техніки, і все застаріло.
Надер Ганбарі

Відповіді:


242

C ++ успадковує справжні сині оператори від C. Під цим я маю на увазі, що "+" в 6 + 4 є дуже особливим. Наприклад, ви не можете отримати вказівник на цю функцію +.

Scala, з іншого боку, не має операторів таким чином. Він просто володіє великою гнучкістю у визначенні назв методів плюс трохи вбудований пріоритет для несловесних символів. Тож технічно Scala не має перевантаження оператора.

Як би ви не хотіли це назвати, перевантаження оператора не є по суті поганою, навіть у C ++. Проблема полягає в тому, коли погані програмісти зловживають цим. Але, чесно кажучи, я вважаю, що віднімання у програмістів можливості зловживати перевантаженням операторів не дає краплі у виправлення всіх речей, які програмісти можуть зловживати. Справжня відповідь - наставництво. http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html

Незважаючи на це, існують відмінності між перевантаженням оператора C ++ та гнучким методом Scala, називаючи який, IMHO, робить Scala як менш зловживаною, так і більш зловживаною.

У C ++ єдиний спосіб отримати вбудовану нотацію - це використання операторів. В іншому випадку потрібно використовувати object.message (argument) або pointer-> messsage (argument) або function (argument1, argument2). Тож якщо ви хочете до свого коду певного стилю DSLish, тоді тиск на використання операторів.

У програмі Scala ви можете отримати позначення інфіксу з будь-яким надсиланням повідомлення. "аргумент об'єктного повідомлення" є цілком нормальним, це означає, що вам не потрібно використовувати несловові символи, щоб отримати позначення інфіксу.

Перевантаження C ++ операторів обмежено по суті операторами C. У поєднанні з обмеженням, що тільки оператори можуть використовуватись інфіксом, який тисне на людей, щоб спробувати відобразити широкий спектр непов'язаних понять на порівняно небагато символів, таких як "+" та ">>"

Scala дозволяє отримати величезний діапазон дійсних несловесних символів як назв методів. Наприклад, у мене вбудований DSL Prolog-ish, куди можна писати

female('jane)!         // jane is female
parent('jane,'john)!   // jane is john's parent
parent('jane, 'wendy)! // jane is wendy's parent

mother('Mother, 'Child) :- parent('Mother, 'Child) & female('Mother) //'// a mother of a child is the child's parent and is female

mother('X, 'john)?  // find john's mother
mother('jane, 'X)?  // find's all of jane's children

Символи: -,!,? І & визначаються як звичайні методи. Тільки для C ++ & було б дійсно, тому для спроби відображення цього DSL в C ++ потрібні деякі символи, які вже викликають дуже різні поняття.

Звичайно, це також відкриває Scala до іншого виду зловживань. У Scala ви можете назвати метод $! & ^%, Якщо ви хочете.

Для інших мов, які, як і Scala, є гнучкими у використанні несловових функцій та імен методів, див. Smalltalk, де, як і Scala, кожен "оператор" - це лише інший метод, і Haskell, який дозволяє програмісту визначати пріоритетність та фіксацію гнучко названих функції.


Останній я перевірив, 3.оператор + (5) працював. Я дуже здивований, що & (3.operator +) не робить.
Джошуа

Ви можете, наприклад, зробити tvrd (жіночий ("jane")) в c ++. Це зовсім не бентежить - кивнути назад до публікації про Джеймс-Айрі, що це не те, що оператор + це погана річ, але дурні програмісти є.
pm100

1
@Joshua int main() {return (3).operator+(5);}призводить доerror: request for member ‘operator+’ in ‘3’, which is of non-class type ‘int’
zildjohn01

Це купа зарозумілих лайнів: "Перевантаження операторів не є по своїй суті поганою, навіть в C ++. Проблема полягає в тому, коли погані програмісти зловживають цим". Якщо щось легко зловживати з досить невеликою користю від його використання, загальний результат полягає в тому, що наступний хлопець, який підтримує ваш код, втратить продуктивність при розшифровці більш слабких частин вашого коду. Інакше: Дуже інформативна та добре написана відповідь.
Jukka Dahlbom

@JukkaDahlbom Наявність розумних покажчиків робить користь великою самостійно. І тоді у вас є лямбда, визначені користувачем типи чисел, типи інтервалів ...
Олексій Романов

66

Перевантаження оператора в C ++ багато хто вважає поганою річчю (тм)

Тільки невігласом. Це абсолютно потрібно в такій мові, як C ++, і помітно, що інші мови, які почали приймати "пуристський" погляд, додали його, як тільки їх дизайнери дізналися, наскільки це потрібно.


30
Я насправді згоден з Нілом. Перевантаження оператора є надзвичайно важливим, якщо ви хочете представити змінні / константи / об'єкти / екземпляри як алгебраїчні утворення ... і щоб люди розуміли їх взаємодію математично - що повинно працювати як програмування IMHO.
Масса

16
+1, Перевантаження оператора в C ++ - це добре. Наприклад, це робить векторну математику набагато чистішою. Як і в багатьох функціях C ++, вам слід обережно користуватися живленням.
Джон Сміт

7
@ Kristo Оскільки C ++ використовує значення, які потрібно призначити та скопіювати. Необхідно мати контроль над цим, тому ви повинні мати можливість вказати оператора присвоєння для даного типу, як мінімум.

7
@ Kristo: оскільки одна мета C ++ - дозволити визначеним користувачем типам робити все, що роблять вбудовані типи (хоча в деяких контекстах, таких як неявні перетворення), вони трактуються по-різному. Якщо ви хочете реалізувати 27-бітове ціле число, тоді ви можете, і використовувати його буде так само, як використання int. Без перевантаження оператора неможливо використовувати UDT з тим самим синтаксисом, що і вбудовані типи, і, отже, отримана мова не була б "як C ++" у цьому сенсі.
Стів Джессоп

8
"так лежить божевілля" - ще гірше, так лежить std :: vector <bool>!
Стів Джессоп

42

Перевантаження з боку операторів ніколи не вважалося поганою ідеєю в C ++ - просто зловживання оператором перевантаження вважалося поганою ідеєю. Насправді не потрібна перевантаження оператора мовою, оскільки їх можна моделювати за допомогою більш детальних викликів функцій. Уникнення перевантаження оператора в Java зробило реалізацію та специфікацію Java трохи простішою, і це змусило програмістів не зловживати операторами. У спільноті Java були дебати щодо запровадження перевантаження оператора.

Переваги та недоліки перевантаження оператора в Scala такі ж, як і в C ++ - ви можете написати більш природний код, якщо правильно використовувати перевантаження оператора - і більш чіткий, затуманений код, якщо цього не зробите.

FYI: Оператори не визначені як спеціальні функції в C ++, вони поводяться так само, як і будь-яка інша функція - хоча існують деякі відмінності в пошуку імен, чи потрібно їм бути членами, і те, що їх можна викликати двома способами: 1 ) синтаксис оператора та 2) синтаксис operator-function-id.


"Один дійсно не потребує перевантаження оператора мовою, оскільки їх можна імітувати за допомогою більш багатослівних викликів функцій." Один навіть не потребує операторів за цією логікою. Чому б просто не використовувати add(2, multiply(5, 3))?
Джо З.

Це скоріше збіг звичних позначень, що використовуються. Розглянемо математиків та фізиків, вони можуть зрозуміти та використовувати бібліотеку C ++, яка набагато легше забезпечує перевантаження операторів. Вони скоріше зосередитимуться на рівнянні, ніж на мові програмування.
Філ Райт

19

Ця стаття - " Позитивна спадщина C ++ та Java " - відповідає безпосередньо на ваше запитання.

"У C ++ є розподіл стеків і розподіл купи, і ви повинні перевантажувати своїх операторів для вирішення всіх ситуацій і не спричиняти витоків пам'яті. Дійсно, важко. Java, однак, має єдиний механізм розподілу пам’яті та збирач сміття, що робить перевантаження оператора тривіальною". ..

Java помилково (за словами автора) пропустила перевантаження оператора, оскільки це було складним в C ++, але забула, чому (або не зрозуміла, що не застосовується до Java).

На щастя, мови вищого рівня, як Scala, дають розробникам параметри, поки вони працюють на тому ж JVM.


14
Eckel - єдине джерело, яке я коли-небудь бачив за ідею, що перевантаження оператора було вимкнено з Java через ускладнення в C ++, і він не каже, що його джерело. Я би знизив його. У всіх інших джерелах, про які я розповідаю, це було зруйновано через потенційні зловживання. Дивіться gotw.ca/publications/c_family_interview.htm та newt.com/wohler/articles/james-gosling-ramblings-1.html . Просто пошукайте їх на "перевантаження оператора".
Джеймс Ірі

9

З перевантаженням оператора немає нічого поганого. Насправді, щось не так, якщо оператор не перевантажує числові типи. (Погляньте на деякий код Java, який використовує BigInteger та BigDecimal.)

Однак у C ++ є традиція зловживати функцією. Приклад, що часто цитується, є те, що оператори bitshift перевантажені робити I / O.


Оператори << і >> візуально вказують спосіб передачі, вони призначені для вводу / виводу, це не зловживання, це зі стандартної бібліотеки та практичної речі. Подивіться лише на "cin >> щось", що куди йде? Від кінь, до чогось, очевидно.
горіх

7
@peenut: Але їх первісне використання було зсувним. "Стандартна бібліотека" використовує оператор таким чином, що повністю змішується з початковим визначенням.
Джо З.

1
Я впевнений, що десь читав, що Bjarne Stroustrup (творець C ++) експериментував із використанням =замість <<та >>на початку C ++, але зіткнувся з проблемами, оскільки він не мав правильного пріоритету оператора (тобто чи шукає він аргументи спочатку зліва чи справа). Тож його руки були трохи зав'язані з приводу того, що він може використати.
Філ Райт

8

Взагалі це не погано.
Нові мови, такі як C #, також мають перевантаження оператора.

Це зловживання операторами перевантаження, це погано.

Але також є проблеми з перевантаженням оператора, як визначено в C ++. Оскільки оператори, що перевантажують, є лише синтаксичним цукром для викликів методів, вони поводяться так само, як метод. З іншого боку, звичайні вбудовані оператори не ведуть себе як методи. Ці непослідовність можуть спричинити проблеми.

Зверху моїх головних операторів ||і &&.
Вбудовані їх версії - це оператори, що скорочуються. Це не стосується перевантажених версій і спричинило деякі проблеми.

Справа в тому, що + - * / всі повертають той самий тип, з яким вони працюють (після просування оператора)
Перевантажені версії можуть повернути що завгодно (саме тут встановлюється зловживання, якщо ваші оператори почнуть повертати якийсь тип арбітра, користувач не очікував речі спускаються з пагорба).


8

Перевантаження оператора - це не те, що вам дуже часто "потрібно", але при використанні Java, якщо ви потрапили в точку, коли вам справді це потрібно, це змусить вас зірвати нігті просто так, щоб у вас є привід припинити вводити текст .

Той код, який ви щойно знайшли, переповнює довго? Так, вам доведеться повторно ввести всю партію, щоб вона працювала з BigInteger. Немає нічого більш страшного в тому, що доведеться винаходити колесо лише для зміни типу змінної.


6

Гай Стіл стверджував, що перевантаження оператора має бути і на Яві, у своїй основній промові "Зростання мови" - є відео та транскрипція його, і це справді дивовижна мова. Вам буде цікаво, про що він говорить протягом першої пари сторінок, але якщо ви продовжите читати, ви побачите сенс і досягнете просвітлення. І сам факт, що він взагалі міг зробити таку промову, також дивує.

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

Назагалі, його приклади здебільшого стосуються числових класів (наприклад, BigInteger та деякі більш дивні речі), але це не суттєво.

Правда, що неправильне використання перевантаження оператором може призвести до жахливих результатів, і навіть правильне використання може ускладнити питання, якщо ви спробуєте прочитати код, не вивчаючи трохи бібліотек, які він використовує. Але це гарна ідея? OTOH, чи не повинні такі бібліотеки намагатися включити шпаргалку операторів для своїх операторів?


4

Я вважаю, КОЖНА відповідь пропустила це. У C ++ ви можете перевантажувати операторів усім, що вам потрібно, але ви не можете впливати на пріоритет, за яким вони оцінюються. У Scala цього питання немає, IIRC.

Що стосується поганої ідеї, окрім питань пріоритетності, люди придумують дійсно важливі значення для операторів, і це рідко сприяє читанні. Бібліотеки Scala особливо погані для цього, тугі символи, які ви повинні запам’ятовувати кожен раз, а технічні працівники бібліотек вставляють голову в пісок, говорячи: «вам це потрібно вивчити лише один раз». Чудово, зараз мені потрібно вивчити криптичний синтаксис 'розумного' автора * кількість бібліотек, якими я хотів би користуватися. Було б не так вже й погано, якби завжди існувала конвенція ЗАВЖДИ, яка постачала грамотну версію операторів.


1
Scala має фіксований пріоритет оператора, чи не так?
skaffman

Я вважаю, що це є, але це набагато більш просто. Більш того, у Scala менше операторського періоду. +, -, * - це методи, а не оператори, IIRC. Ось чому 2 + 3 * 2, не 8, це 10.
Saem

7
Scala має систему пріоритету, засновану на першому символі символу. scala> 2 + 3 * 2 res0: Int = 8
Джеймс Ірі

3

Перевантаження оператора не було винаходом C ++ - воно походить від Algol IIRC і навіть Гослінг не стверджує, що це взагалі погана ідея.


Зрозуміло, але саме у своєму втіленні C ++ він отримав загальну атмосферу неповаги.
skaffman

5
Що ви маєте на увазі під загальним висловом неповаги? Більшість людей, яких я знаю, використовують мови, які підтримують перевантаження оператора (C ++, C #), і я ніколи не чув жодних скарг.
Неманья Трифунович

Я кажу з давнього досвіду роботи з попереднім ANSI C ++, і я, звичайно, згадую загальну неприязнь до них. Можливо, ситуація покращилася з ANSI C ++, або люди просто навчилися не зловживати цим.
скафман

1
Виступаючи, як хтось, хто використовує C ++ ще з перших днів (середина 80-х років), я можу запевнити вас, що введення стандарту ISO не вплинуло на забобони людей щодо перевантаження операторів.

3

Єдине, що в C ++ відомо неправильно, - це відсутність можливості перевантажувати [] = як окремий оператор. Це може бути важко реалізувати в компіляторі C ++, тому що це, мабуть, не очевидна причина, але багато чого варте.


2

Як вказували інші відповіді; Перевантаження оператора сама по собі не обов'язково погано. Що поганого, коли він використовується таким чином, що робить отриманий код не очевидним. Як правило, при їх використанні вам потрібно змусити їх зробити найменш дивовижну справу (наявність оператора + do подіє б проблему для раціонального використання класу) або, як каже Скотт Майєрс:

Клієнти вже знають, як поводяться такі типи, як int, тому вам слід прагнути, щоб ваші типи поводилися так само, коли це розумно ... Коли ви сумніваєтесь, робіть так, як це роблять ints . (З ефективного C ++ 3-го видання, пункт 18)

Зараз деякі люди переймаються перевантаженням операторів до крайності такими речами, як boost :: spirit . На цьому рівні ви не маєте уявлення про те, як він реалізований, але він робить цікавий синтаксис, щоб зробити те, що ви хочете зробити. Я не впевнений, добре це чи погано. Це здається приємним, але я його не використовував.


Я не сперечаюся за або проти перевантаження оператора тут, я не шукаю людей, щоб їх виправдати.
skaffman

Спринт ніде не зустрічається з найгіршим прикладом, на який я натрапив - ви повинні побачити, до чого потрапляє бібліотека даних RogueWave!

Я погоджуюся, що Дух зловживає операторами, але я не можу реально придумати кращий спосіб зробити це.
Зіфре

1
Я не думаю, що дух дуже зловживає операторами, але це підштовхує. Я погоджуюся, що насправді іншого способу це зробити немає. В основному це створює DSL в синтаксисі C ++. Дуже далеко від того, що C ++ був розроблений. Так, є куди гірші приклади :) Загалом я їх використовую, де це доречно. Здебільшого просто оператори потокового передавання для легшої налагодження \ ведення журналів. І навіть там просто цукор переходить до методу, реалізованого в класі.
Метт Ціна

1
Це питання смаку; але бібліотеки комбінаторів-аналізаторів на функціональних мовах перевантажують операторів таким чином, дуже схожим на Spirit, і ніхто не заперечує проти цього. Існує багато технічних причин, для яких вони кращі - Google для "вбудованих доменних мов", щоб знайти безліч паперів, що пояснюють це з загальної точки зору, а Google для "скала-парсер-комбінатора" для практичних прикладів у цьому випадку. Це правда, що у функціональних мовах одержуваний синтаксис часто приємніший - наприклад, вам не потрібно змінювати значення >> для об'єднання парсерів.
Blaisorblade

2

Я ніколи не бачив статті, яка стверджує, що перевантаження оператора C ++ погана.

Оператори, визначені користувачем, дозволяють легше підвищити рівень виразності та зручності користування для користувачів мови.


1

Однак це, здається, не буде якісно відрізнятися від перевантаження оператора в C ++, де, наскільки я пам'ятаю, оператори визначаються як спеціальні функції.

AFAIK. У функціях оператора немає нічого особливого порівняно з "нормальними" функціями членів. Звичайно, у вас є лише певний набір операторів, яким ви можете перевантажувати, але це не робить їх особливими.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.