Qu'est-ce qui rend la surcharge des opérateurs de Scala «bonne», mais «mauvaise» de C ++?

155

La surcharge d'opérateurs en C ++ est considérée par beaucoup comme une mauvaise chose (tm), et une erreur à ne pas répéter dans les nouveaux langages. Il s'agissait certainement d'une fonctionnalité spécifiquement abandonnée lors de la conception de Java.

Maintenant que j'ai commencé à lire sur Scala, je trouve qu'il a ce qui ressemble beaucoup à une surcharge d'opérateurs (bien que techniquement, il ne soit pas surchargé car il n'a pas d'opérateurs, seulement des fonctions). Cependant, cela ne semble pas être qualitativement différent de la surcharge d'opérateurs en C ++, où, si je me souviens bien, les opérateurs sont définis comme des fonctions spéciales.

Ma question est donc de savoir ce qui fait de l'idée de définir "+" dans Scala une meilleure idée qu'elle ne l'était en C ++?

skaffman
la source
27
Ni C ++ ni Scala n'ont été définis par consensus universel parmi tous les programmeurs. Je ne pense pas qu'il y ait de contradiction entre le fait que certaines personnes se plaignent de C ++ et le fait que certaines personnes ne se plaignent pas de Scala.
Steve Jessop
16
Il n'y a rien de mauvais à propos de la surcharge d'opérateurs en C ++.
Puppy
5
Ce n'est pas nouveau mais la façon dont je défends le C ++ lorsque la surcharge des opérateurs et d'autres fonctionnalités "avancées" sont remises en question est simple: C ++ nous donne tout le pouvoir de l'utiliser / abuser comme bon nous semble. J'ai toujours aimé la façon dont nous sommes supposés être compétents et autonomes et que nous n'avons pas besoin de décisions comme celle-ci pour nous.
Elliott
Scala a été conçu comme des décennies après le C ++. Il s'avère que la personne derrière est super savante en termes de langages de programmation. Rien de mauvais en soi non plus, si vous vous en tenez à C ++ ou Scala pendant encore 100 ans, il devient clair que les deux sont probablement mauvais! Être partial est apparemment dans notre nature mais nous pouvons le combattre, il suffit de regarder l'histoire de la technologie, tout devient obsolète.
Nader Ghanbari

Réponses:

242

C ++ hérite des véritables opérateurs bleus de C. J'entends par là que le "+" en 6 + 4 est très spécial. Vous ne pouvez pas, par exemple, obtenir un pointeur vers cette fonction +.

Scala, en revanche, n'a pas d'opérateurs de cette manière. Il a juste une grande flexibilité dans la définition des noms de méthode et un peu de priorité intégrée pour les symboles non-mot. Donc, techniquement, Scala n'a pas de surcharge d'opérateurs.

Quoi que vous vouliez l'appeler, la surcharge d'opérateurs n'est pas intrinsèquement mauvaise, même en C ++. Le problème est que les mauvais programmeurs en abusent. Mais franchement, je suis d'avis que priver les programmeurs de la capacité d'abuser de la surcharge des opérateurs ne met pas une goutte dans le seau de réparer toutes les choses dont les programmeurs peuvent abuser. La vraie réponse est le mentorat. http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html

Néanmoins, il existe des différences entre la surcharge d'opérateurs de C ++ et la dénomination de méthode flexible de Scala qui, à mon humble avis, rend Scala à la fois moins abusable et plus abusable.

En C ++, le seul moyen d'obtenir une notation in-fix est d'utiliser des opérateurs. Sinon, vous devez utiliser object.message (argument) ou pointer-> messsage (argument) ou function (argument1, argument2). Donc, si vous voulez un certain style DSL pour votre code, il y a une pression pour utiliser des opérateurs.

Dans Scala, vous pouvez obtenir une notation infixe avec n'importe quel message envoyé. "object message argument" est parfaitement correct, ce qui signifie que vous n'avez pas besoin d'utiliser des symboles non-mot juste pour obtenir la notation infixe.

La surcharge des opérateurs C ++ est essentiellement limitée aux opérateurs C. Combiné avec la limitation que seuls les opérateurs peuvent être utilisés infixe qui met la pression sur les gens pour essayer de mapper un large éventail de concepts non liés sur un nombre relativement limité de symboles comme "+" et ">>"

Scala autorise une vaste gamme de symboles valides sans mots comme noms de méthode. Par exemple, j'ai un DSL Prolog-ish intégré où vous pouvez écrire

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

Les symboles: -,!,? Et & sont définis comme des méthodes ordinaires. En C ++ seulement & serait valide donc une tentative de mapper ce DSL en C ++ nécessiterait des symboles qui évoquent déjà des concepts très différents.

Bien sûr, cela ouvre également Scala à un autre type d'abus. Dans Scala, vous pouvez nommer une méthode $! & ^% Si vous le souhaitez.

Pour d'autres langages qui, comme Scala, sont flexibles dans l'utilisation de fonctions et de noms de méthodes non-mot, voir Smalltalk où, comme Scala, chaque "opérateur" est juste une autre méthode et Haskell qui permet au programmeur de définir la priorité et la fixité des noms flexibles les fonctions.

James Iry
la source
La dernière fois que j'ai vérifié, 3. opérateur + (5) a fonctionné. Je suis vraiment surpris que & (3.opérateur +) ne le fasse pas.
Joshua
vous pouvez par exemple faire assert (female ("jane")) en c ++. Ce ne serait pas déroutant du tout - faites un clin d'œil au message de James-iry sur le fait que l'opérateur + n'est pas une mauvaise chose, mais les programmeurs stupides le sont.
pm100
1
int main() {return (3).operator+(5);}Résultats @Joshuaerror: request for member ‘operator+’ in ‘3’, which is of non-class type ‘int’
zildjohn01
C'est un tas de conneries arrogantes: "la surcharge d'opérateurs n'est pas intrinsèquement mauvaise, même en C ++. Le problème est que les mauvais programmeurs en abusent." Si quelque chose est facilement abusable avec assez peu d'avantages à l'utiliser, le résultat global est que le prochain responsable de la maintenance de votre code perdra de la productivité en déchiffrant les parties les plus étranges de votre code. Sinon: réponse très informative et bien écrite.
Jukka Dahlbom
@JukkaDahlbom L'existence de pointeurs intelligents rend l'avantage important en soi. Et puis vous avez des lambdas, des types de nombres définis par l'utilisateur, des types d'intervalle ...
Alexey Romanov
66

La surcharge d'opérateurs en C ++ est considérée par beaucoup comme une mauvaise chose (tm)

Seulement par l'ignorant. Il est absolument nécessaire dans un langage comme le C ++, et il est à noter que d'autres langages qui avaient commencé par adopter une vision «puriste» l'ont ajouté une fois que leurs concepteurs ont découvert à quel point c'était nécessaire.


la source
30
Je suis en fait d'accord avec Neil. La surcharge des opérateurs est essentielle si vous souhaitez présenter des variables / constantes / objets / instances comme des entités algébriques ... et faire comprendre aux gens leurs interactions de manière mathématique - ce qui devrait être la façon dont la programmation fonctionne à mon humble avis.
Massa
16
+1, la surcharge des opérateurs en C ++ est bonne. Par exemple, cela rend les mathématiques vectorielles beaucoup plus propres. Comme avec de nombreuses fonctionnalités C ++, vous devez utiliser le pouvoir avec précaution.
John Smith
7
@Kristo Parce que C ++ utilise des valeurs qui doivent être affectées et copiées. Il est nécessaire d'avoir le contrôle sur cela, vous devez donc être en mesure de spécifier l'opérateur d'affectation pour un type donné, au minimum.
7
@Kristo: parce qu'une intention de C ++ est de permettre aux types définis par l'utilisateur de faire tout ce que font les types intégrés (bien qu'ils soient traités différemment dans certains contextes tels que les conversions implicites). Si vous voulez implémenter un entier de 27 bits, alors vous pouvez, et l'utiliser sera comme utiliser int. Sans surcharge d'opérateurs, il ne serait pas possible d'utiliser des UDT avec la même syntaxe que les types intégrés, et par conséquent, le langage résultant ne serait pas «comme C ++» dans ce sens.
Steve Jessop
8
"de cette façon est la folie" - pire encore, de cette façon se trouve std :: vector <bool>!
Steve Jessop le
42

La surcharge des opérateurs n'a jamais été universellement considérée comme une mauvaise idée en C ++ - juste l'abus de la surcharge des opérateurs était considéré comme une mauvaise idée. On n'a pas vraiment besoin de surcharge d'opérateurs dans un langage car ils peuvent quand même être simulés avec des appels de fonctions plus verbeux. Éviter la surcharge des opérateurs en Java a rendu la mise en œuvre et la spécification de Java un peu plus simples et obligé les programmeurs à ne pas abuser des opérateurs. Il y a eu un débat dans la communauté Java sur l'introduction de la surcharge des opérateurs.

Les avantages et les inconvénients de la surcharge d'opérateurs dans Scala sont les mêmes qu'en C ++ - vous pouvez écrire du code plus naturel si vous utilisez la surcharge d'opérateurs de manière appropriée - et du code plus crypté et obscurci si vous ne le faites pas.

FYI: Les opérateurs ne sont pas définis comme des fonctions spéciales en C ++, ils se comportent comme n'importe quelle autre fonction - bien qu'il y ait quelques différences dans la recherche de nom, s'ils doivent être des fonctions membres et le fait qu'ils peuvent être appelés de deux manières: 1 ) syntaxe d'opérateur, et 2) syntaxe d'ID de fonction d'opérateur.

Faisal Vali
la source
"On n'a pas vraiment besoin de surcharge d'opérateurs dans un langage car ils peuvent de toute façon être simulés avec des appels de fonctions plus verbeux." On n'a même pas vraiment besoin d' opérateurs dans cette logique. Pourquoi ne pas simplement utiliser add(2, multiply(5, 3))?
Joe Z.28
Il s'agit plutôt de faire correspondre les notations habituelles utilisées. Considérons les mathématiciens et les physiciens, ils peuvent comprendre et utiliser une bibliothèque C ++ qui fournit beaucoup plus facilement des surcharges d'opérateurs. Ils préfèrent se concentrer sur l'équation plutôt que sur le langage de programmation.
Phil Wright
19

Cet article - « L'héritage positif de C ++ et Java » - répond directement à votre question.

"C ++ a à la fois une allocation de pile et une allocation de tas et vous devez surcharger vos opérateurs pour gérer toutes les situations et ne pas provoquer de fuites de mémoire. Difficile en effet. Java, cependant, a un mécanisme d'allocation de stockage unique et un ramasse-miettes, ce qui rend la surcharge des opérateurs triviale". ..

Java a par erreur (selon l'auteur) omis la surcharge d'opérateurs parce que c'était compliqué en C ++, mais a oublié pourquoi (ou n'a pas réalisé que cela ne s'appliquait pas à Java).

Heureusement, les langages de niveau supérieur comme Scala offrent aux développeurs des options, tout en fonctionnant toujours sur la même JVM.

jmanning2k
la source
14
Eckel est la seule source que j'ai jamais vue pour l'idée que la surcharge d'opérateurs a été abandonnée de Java en raison de complications en C ++ et il ne dit pas quelle est sa source. Je voudrais le réduire. Toutes les autres sources que j'ai disent qu'il a été abandonné en raison d'abus potentiels. Voir gotw.ca/publications/c_family_interview.htm et newt.com/wohler/articles/james-gosling-ramblings-1.html . Il suffit de les rechercher sur la page pour "surcharge d'opérateurs".
James Iry
9

Il n'y a rien de mal à surcharger l'opérateur. En fait, il y a quelque chose de mal à ne pas avoir de surcharge d'opérateurs pour les types numériques. (Jetez un œil à du code Java qui utilise BigInteger et BigDecimal.)

Cependant, C ++ a une tradition d'abuser de cette fonctionnalité. Un exemple souvent cité est que les opérateurs de décalage de bits sont surchargés pour effectuer des E / S.

dan04
la source
Les opérateurs << et >> indiquent visuellement le mode de transfert, ils sont destinés à faire des E / S, ce n'est pas un abus, c'est de la bibliothèque standard et des choses pratiques. Regardez "cin >> quelque chose", qu'est-ce qui va où? De cin, à quelque chose, évidemment.
peenut
7
@peenut: Mais leur utilisation initiale était un peu changeante. La "bibliothèque standard" utilise l'opérateur d'une manière qui dérange complètement la définition d'origine.
Joe Z.28
1
Je suis sûr que j'ai lu quelque part que Bjarne Stroustrup (créateur de C ++) a expérimenté l'utilisation à la =place <<et >>aux premiers jours de C ++, mais a rencontré des problèmes car il n'avait pas la bonne priorité d'opérateur (c'est-à-dire qu'il recherche arguments à gauche ou à droite en premier). Donc, ses mains étaient un peu liées sur ce qu'il pouvait utiliser.
Phil Wright
8

En général, ce n'est pas une mauvaise chose.
Les nouveaux langages tels que C # ont également une surcharge d'opérateurs.

C'est l'abus de surcharge de l'opérateur qui est une mauvaise chose.

Mais il y a aussi des problèmes de surcharge d'opérateurs tels que définis dans C ++. Parce que les opérateurs surchargés ne sont que du sucre syntaxique pour les appels de méthode, ils se comportent comme une méthode. D'un autre côté, les opérateurs intégrés normaux ne se comportent pas comme des méthodes. Ces incohérences peuvent poser des problèmes.

Sur le dessus de mes opérateurs de tête ||et &&.
Les versions intégrées de ceux-ci sont des opérateurs de raccourcis. Cela n'est pas vrai pour les versions surchargées et a causé quelques problèmes.

Le fait que + - * / retournent tous le même type sur lequel ils opèrent (après la promotion de l'opérateur)
Les versions surchargées peuvent renvoyer n'importe quoi (c'est là que l'abus s'installe, si vos opérateurs commencent à renvoyer un type d'arbitre auquel l'utilisateur ne s'attendait pas) les choses descendent).

Martin York
la source
8

La surcharge des opérateurs n'est pas quelque chose dont vous "avez vraiment besoin" très souvent, mais lorsque vous utilisez Java, si vous atteignez un point où vous en avez vraiment besoin, cela vous donnera envie de vous arracher les ongles juste pour que vous ayez une excuse pour arrêter de taper .

Ce code que vous venez de trouver déborde longtemps? Oui, vous allez devoir retaper le tout pour que cela fonctionne avec BigInteger. Il n'y a rien de plus frustrant que de devoir réinventer la roue juste pour changer le type d'une variable.


la source
6

Guy Steele a fait valoir que la surcharge des opérateurs devrait également être en Java, dans son discours d'ouverture "Cultiver un langage" - il y a une vidéo et une transcription de celui-ci, et c'est vraiment un discours incroyable. Vous vous demanderez de quoi il parle pour les deux premières pages, mais si vous continuez à lire, vous verrez le point et atteindrez l'illumination. Et le fait même qu'il puisse faire un tel discours est également incroyable.

Dans le même temps, cette conférence a inspiré de nombreuses recherches fondamentales, y compris probablement Scala - c'est l'un de ces articles que tout le monde devrait lire pour travailler sur le terrain.

Pour revenir au point, ses exemples concernent principalement des classes numériques (comme BigInteger, et des trucs plus étranges), mais ce n'est pas essentiel.

Il est vrai, cependant, qu'une mauvaise utilisation de la surcharge d'opérateurs peut conduire à des résultats terribles, et que même des utilisations appropriées peuvent compliquer les choses, si vous essayez de lire du code sans étudier un peu les bibliothèques qu'il utilise. Mais est-ce une bonne idée? OTOH, ces bibliothèques ne devraient-elles pas essayer d'inclure une feuille de triche pour leurs opérateurs?

Blaisorblade
la source
4

Je crois que CHAQUE réponse a manqué cela. En C ++, vous pouvez surcharger les opérateurs autant que vous le souhaitez, mais vous ne pouvez pas affecter la priorité avec laquelle ils sont évalués. Scala n'a pas ce problème, IIRC.

Quant à ce que ce soit une mauvaise idée, outre les problèmes de priorité, les gens ont des significations vraiment stupides pour les opérateurs, et cela facilite rarement la lisibilité. Les bibliothèques Scala sont particulièrement mauvaises pour cela, des symboles loufoques que vous devez mémoriser à chaque fois, les responsables de la bibliothèque collant la tête dans le sable en disant: «vous n'avez besoin de l'apprendre qu'une seule fois». Génial, maintenant j'ai besoin d'apprendre la syntaxe cryptique d'un auteur «intelligent» * le nombre de bibliothèques que je souhaite utiliser. Ce ne serait pas si grave s'il existait une convention de TOUJOURS fournir une version alphabétisée des opérateurs.

Saem
la source
1
Scala a également une priorité fixe des opérateurs, n'est-ce pas?
skaffman le
Je crois que oui, mais c'est beaucoup plus plat. Plus précisément, Scala a moins de période d'opérateurs. +, -, * sont des méthodes, pas des opérateurs, IIRC. C'est pourquoi 2 + 3 * 2, n'est pas 8, c'est 10.
Saem le
7
Scala a un système de priorité basé sur le premier caractère du symbole. scala> 2 + 3 * 2 res0: Int = 8
James Iry
3

La surcharge d'opérateurs n'était pas une invention C ++ - elle venait d'Algol IIRC et même Gosling ne prétend pas que c'est une mauvaise idée en général.

Nemanja Trifunovic
la source
Bien sûr, mais c'est dans son incarnation C ++ qu'il a acquis un air général de manque de réputation.
skaffman
5
Qu'entendez-vous par «air général de manque de réputation»? La plupart des gens que je connais utilisent des langages qui prennent en charge la surcharge d'opérateurs (C ++, C #) et je n'ai jamais entendu de plaintes.
Nemanja Trifunovic
Je parle de ma longue expérience avec le C ++ pré-ANSI, et je me souviens certainement d'une aversion commune à leur égard. Peut-être que la situation s'est améliorée avec ANSI C ++ ou que les gens ont juste appris à ne pas en abuser.
skaffman
1
Parlant en tant que personne qui utilise C ++ depuis les jours de cfront (milieu des années 80), je peux vous assurer que l'introduction de la norme ISO n'a eu aucun effet sur les préjugés des gens concernant la surcharge des opérateurs.
3

La seule chose mal connue en C ++ est le manque de capacité à surcharger [] = en tant qu'opérateur séparé. Cela pourrait être difficile à implémenter dans un compilateur C ++ pour ce qui n'est probablement pas une raison évidente mais qui en vaut largement la peine.

Joshua
la source
2

Comme les autres réponses l'ont souligné; la surcharge de l'opérateur lui-même n'est pas nécessairement mauvaise. Ce qui est mauvais quand il est utilisé de manière à rendre le code résultant peu évident. Généralement, lorsque vous les utilisez, vous devez leur faire faire la chose la moins surprenante (avoir opérateur + do division causerait des problèmes pour l'utilisation d'une classe rationnelle) ou comme le dit Scott Meyers:

Les clients savent déjà comment les types comme int se comportent, vous devriez donc vous efforcer de faire en sorte que vos types se comportent de la même manière chaque fois que cela est raisonnable ... En cas de doute, faites comme les ints . (À partir du point 18 de la 3e édition de Effective C ++)

Maintenant, certaines personnes ont poussé la surcharge d'opérateurs à l'extrême avec des choses comme boost :: spirit . À ce niveau, vous n'avez aucune idée de la façon dont il est implémenté mais cela crée une syntaxe intéressante pour obtenir ce que vous voulez. Je ne sais pas si c'est bon ou mauvais. Cela semble bien, mais je ne l'ai pas utilisé.

Prix ​​de Matt
la source
Je ne suis pas en train de plaider pour ou contre la surcharge des opérateurs ici, je ne cherche pas de personnes pour les justifier.
skaffman
Sprint n'approche pas du pire exemple que j'ai rencontré - vous devriez voir ce que la bibliothèque de base de données RogueWave fait!
Je conviens que Spirit abuse des opérateurs, mais je ne peux pas vraiment penser à une meilleure façon de le faire.
Zifre
1
Je ne pense pas que l'esprit abuse vraiment des opérateurs, mais il le pousse. Je suis d'accord qu'il n'y a vraiment pas d'autre moyen de le faire. Il crée essentiellement un DSL dans la syntaxe de C ++. Très loin de ce pour quoi le C ++ a été conçu. Oui, il y a des exemples bien pires :) En général, je les utilise là où c'est approprié. Surtout juste les opérateurs de streaming pour un débogage / journalisation plus facile. Et même là, c'est juste du sucre qui transmet à une méthode implémentée dans la classe.
Matt Price
1
C'est une question de goût; mais les bibliothèques de combinateurs d'analyseurs, dans les langages fonctionnels, surchargent les opérateurs d'une manière très similaire à Spirit, et personne ne s'y oppose. Il y a beaucoup de raisons techniques pour lesquelles ils sont meilleurs - Google pour les "langages spécifiques à un domaine intégré" pour trouver de nombreux articles expliquant cela d'un point de vue général, et Google pour "scala parser combinator" pour des exemples pratiques dans ce cas. Il est vrai que dans les langages fonctionnels, la syntaxe résultante est souvent plus agréable - par exemple, vous n'avez pas besoin de changer la signification de >> pour concaténer les analyseurs.
Blaisorblade
2

Je n'ai jamais vu un article affirmant que la surcharge des opérateurs C ++ est mauvaise.

Les opérateurs définissables par l'utilisateur permettent un plus haut niveau d'expressivité et de convivialité pour les utilisateurs de la langue.

Paul Nathan
la source
1

Cependant, cela ne semble pas être qualitativement différent de la surcharge d'opérateurs en C ++, où, si je me souviens bien, les opérateurs sont définis comme des fonctions spéciales.

AFAIK, Il n'y a rien de spécial dans les fonctions d'opérateur par rapport aux fonctions membres "normales". Bien sûr, vous n'avez qu'un certain ensemble d'opérateurs que vous pouvez surcharger, mais cela ne les rend pas très spéciaux.

John Smith
la source