Cosa rende l'operatore di Scala sovraccarico "buono", ma "cattivo" di C ++?


155

Il sovraccarico dell'operatore in C ++ è considerato da molti come A Bad Thing (tm) e un errore da non ripetere nei linguaggi più recenti. Certamente, è stata una caratteristica specificamente abbandonata durante la progettazione di Java.

Ora che ho iniziato a leggere su Scala, ho scoperto che ha quello che assomiglia molto al sovraccarico dell'operatore (anche se tecnicamente non ha un sovraccarico dell'operatore perché non ha operatori, solo funzioni). Tuttavia, non sembrerebbe essere qualitativamente diverso dal sovraccarico dell'operatore in C ++, dove, come ricordo, gli operatori sono definiti come funzioni speciali.

Quindi la mia domanda è: cosa rende l'idea di definire "+" in Scala un'idea migliore di quella in C ++?


27
Né C ++ né Scala sono stati definiti per consenso universale tra tutti i programmatori. Non penso che ci sia alcuna contraddizione tra il fatto che alcune persone provino per C ++ e il fatto che alcune persone non provino per Scala.
Steve Jessop,

16
Non c'è niente di male nel sovraccarico dell'operatore in C ++.
Cucciolo

5
Questa non è una novità, ma il modo in cui difendo il C ++ quando il sovraccarico dell'operatore e altre funzionalità "avanzate" viene messo in discussione è semplice: il C ++ ci dà tutto il potere di usarlo / abusarlo come meglio crediamo. Mi è sempre piaciuto il modo in cui siamo ritenuti competenti e autonomi e non abbiamo bisogno di decisioni come questa prese per noi.
Elliott,

Scala è stata progettata come decenni dopo c ++. Si scopre che la persona dietro di essa è super savant in termini di linguaggi di programmazione. Niente di male in sé, se ti attieni al c ++ o alla Scala per altri 100 anni, diventa chiaro che probabilmente entrambi sono cattivi! Essere di parte è apparentemente nella nostra natura, ma possiamo combatterlo, basta guardare la storia della tecnologia, tutto diventa obsoleto.
Nader Ghanbari,

Risposte:


242

C ++ eredita i veri operatori blu da C. Con questo intendo che il "+" in 6 + 4 è molto speciale. Ad esempio, non puoi ottenere un puntatore a quella + funzione.

Scala invece non ha operatori in quel modo. Ha solo una grande flessibilità nella definizione dei nomi dei metodi più un po 'di precedenza incorporata per i simboli non di parole. Quindi tecnicamente Scala non ha sovraccarico dell'operatore.

Qualunque cosa tu voglia chiamarlo, il sovraccarico dell'operatore non è intrinsecamente negativo, anche in C ++. Il problema è quando i cattivi programmatori lo abusano. Ma francamente, sono dell'opinione che togliere la capacità dei programmatori di abusare del sovraccarico dell'operatore non metta una goccia nel secchio di sistemare tutto ciò che i programmatori possono abusare. La vera risposta è il mentoring. http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html

Tuttavia, ci sono differenze tra il sovraccarico dell'operatore di C ++ e la flessibile denominazione del metodo di Scala che, IMHO, rende Scala meno abusabile e più abusabile.

In C ++ l'unico modo per ottenere la notazione in-fix è usare gli operatori. Altrimenti è necessario utilizzare object.message (argomento) o pointer-> messsage (argomento) o funzione (argomento1, argomento2). Quindi, se vuoi un certo stile DSLish nel tuo codice, allora c'è la pressione di usare gli operatori.

In Scala è possibile ottenere notazioni infissi con qualsiasi messaggio inviato. "argomento messaggio oggetto" è perfettamente ok, il che significa che non è necessario utilizzare simboli non di parole solo per ottenere la notazione infix.

Il sovraccarico dell'operatore C ++ è limitato essenzialmente agli operatori C. In combinazione con la limitazione che solo gli operatori possono essere utilizzati, infissi che fanno pressione sulle persone per provare a mappare una vasta gamma di concetti non correlati su relativamente pochi simboli come "+" e ">>"

Scala consente una vasta gamma di simboli non di parole validi come nomi di metodi. Ad esempio, ho un DSL Prolog-ish incorporato in cui è possibile scrivere

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

I simboli: -,!,? E & sono definiti come metodi ordinari. Solo in C ++ e sarebbe valido, quindi un tentativo di mappare questo DSL in C ++ richiederebbe alcuni simboli che già evocano concetti molto diversi.

Naturalmente, questo apre anche Scala a un altro tipo di abuso. In Scala puoi nominare un metodo $! & ^% Se vuoi.

Per altre lingue che, come Scala, sono flessibili nell'uso della funzione non-parola e dei nomi dei metodi, vedere Smalltalk dove, come Scala, ogni "operatore" è solo un altro metodo e Haskell che consente al programmatore di definire la precedenza e la fissità di nomi flessibili funzioni.


Ultimo controllo, 3.operator + (5) ha funzionato. Sono davvero sorpreso che & (3.operator +) non lo faccia.
Giosuè,

potresti ad esempio affermare (femmina ("jane")) in c ++. Ciò non sarebbe affatto confuso - annuisci al post di James-Iry sul fatto che non è che l'operatore + è una brutta cosa, ma lo sono stupidi programmatori.
pm100

1
int main() {return (3).operator+(5);}Risultati di @Joshua inerror: request for member ‘operator+’ in ‘3’, which is of non-class type ‘int’
zildjohn01

È un mucchio di merda arrogante: "il sovraccarico dell'operatore non è intrinsecamente negativo, anche in C ++. Il problema è quando i cattivi programmatori lo abusano". Se qualcosa è facilmente abusabile con abbastanza pochi benefici dall'usarlo, il risultato complessivo è che il prossimo ragazzo che mantiene il tuo codice perderà la produttività nel decifrare le parti più strane del tuo codice. Altrimenti: risposta molto istruttiva e ben scritta.
Jukka Dahlbom,

@JukkaDahlbom L'esistenza di puntatori intelligenti aumenta da sola il vantaggio. E poi hai lambda, tipi di numero definiti dall'utente, tipi di intervallo ...
Alexey Romanov,

66

Il sovraccarico dell'operatore in C ++ è considerato da molti come una cosa negativa (tm)

Solo dagli ignoranti. È assolutamente necessario in un linguaggio come il C ++ ed è evidente che altri linguaggi che hanno iniziato ad assumere una visione "purista", l'hanno aggiunto una volta che i loro progettisti hanno scoperto quanto sia necessario.


30
In realtà sono d'accordo con Neil. Il sovraccarico dell'operatore è essenziale se si desidera presentare variabili / costanti / oggetti / istanze come entità algebriche ... e fare in modo che le persone comprendano le loro interazioni in modo matematico, che dovrebbe essere il modo in cui la programmazione funziona IMHO.
Massa,

16
+1, sovraccarico dell'operatore in C ++ è buono. Ad esempio, rende la matematica vettoriale molto più pulita. Come con molte funzionalità C ++, dovresti esercitare la potenza con attenzione.
John Smith,

7
@Kristo Perché C ++ utilizza valori che devono essere assegnati e copiati. È necessario avere il controllo su questo, quindi è necessario essere in grado di specificare l'operatore di assegnazione per un determinato tipo, come minimo.

7
@Kristo: perché un'intenzione di C ++ è quella di consentire ai tipi definiti dall'utente di fare tutto ciò che fanno i tipi predefiniti (sebbene siano trattati in modo diverso in alcuni contesti come conversioni implicite). Se vuoi implementare un numero intero a 27 bit, allora puoi, e usarlo sarà come usare int. Senza l'overloading dell'operatore, non sarebbe possibile utilizzare UDT con la stessa sintassi dei tipi predefiniti, e quindi il linguaggio risultante non sarebbe "come C ++" in questo senso.
Steve Jessop,

8
"in questo modo risiede la follia" - ancora peggio, in questo modo si trova std :: vector <bool>!
Steve Jessop,

42

Il sovraccarico dell'operatore non è mai stato universalmente considerato una cattiva idea in C ++ - solo l'abuso del sovraccarico dell'operatore è stato ritenuto una cattiva idea. Non è davvero necessario il sovraccarico dell'operatore in una lingua poiché possono essere simulati con chiamate di funzioni più dettagliate comunque. Evitare il sovraccarico degli operatori in Java ha reso l'implementazione e le specifiche di Java un po 'più semplici e ha costretto i programmatori a non abusare degli operatori. C'è stato qualche dibattito nella comunità Java sull'introduzione del sovraccarico degli operatori.

I vantaggi e gli svantaggi del sovraccarico dell'operatore in Scala sono gli stessi del C ++ - è possibile scrivere codice più naturale se si utilizza il sovraccarico dell'operatore in modo appropriato - e un codice più criptico e offuscato se non lo si fa.

Cordiali saluti: Gli operatori non sono definiti come funzioni speciali in C ++, si comportano proprio come qualsiasi altra funzione - anche se ci sono alcune differenze nella ricerca dei nomi, se devono essere funzioni membro e il fatto che possono essere chiamati in due modi: 1 ) sintassi operatore e 2) sintassi operatore-funzione-ID.


"Uno non ha davvero bisogno di un sovraccarico da parte dell'operatore in una lingua poiché può comunque essere simulato con chiamate di funzioni più dettagliate." Uno non ha nemmeno davvero bisogno di operatori con quella logica. Perché non usare solo add(2, multiply(5, 3))?
Joe Z.

È più un caso di abbinare le solite notazioni utilizzate. Considera matematici e fisici, possono capire e usare una libreria C ++ che fornisce molto più facilmente i sovraccarichi degli operatori. Preferirebbero concentrarsi sull'equazione piuttosto che sul linguaggio di programmazione.
Phil Wright,

19

Questo articolo - " L'eredità positiva di C ++ e Java " - risponde direttamente alla tua domanda.

"Il C ++ ha sia allocazione di stack che allocazione di heap e devi sovraccaricare i tuoi operatori per gestire tutte le situazioni e non causare perdite di memoria. Difficile in effetti. Java, tuttavia, ha un singolo meccanismo di allocazione dello storage e un garbage collector, che rende banale il sovraccarico degli operatori". ..

Java per errore (secondo l'autore) ha omesso il sovraccarico dell'operatore perché era complicato in C ++, ma ha dimenticato il perché (o non si è reso conto che non si applicava a Java).

Per fortuna, linguaggi di livello superiore come Scala offrono agli sviluppatori opzioni, pur continuando a funzionare sulla stessa JVM.


14
Eckel è l'unica fonte che abbia mai visto per l'idea che il sovraccarico dell'operatore sia stato abbandonato da Java a causa delle complicazioni in C ++ e non dice quale sia la sua fonte. Lo sconto. Tutte le altre fonti che ho detto che è stato abbandonato a causa di potenziali abusi. Vedi gotw.ca/publications/c_family_interview.htm e newt.com/wohler/articles/james-gosling-ramblings-1.html . Basta cercarli nella pagina per "sovraccarico dell'operatore".
James Iry,

9

Non c'è nulla di sbagliato nel sovraccarico dell'operatore. In effetti, c'è qualcosa di sbagliato nel non avere un sovraccarico dell'operatore per i tipi numerici. (Dai un'occhiata ad alcuni codici Java che usano BigInteger e BigDecimal.)

C ++ ha una tradizione di abuso della funzionalità, però. Un esempio spesso citato è che gli operatori bitshift sono sovraccarichi per eseguire l'I / O.


<< >> e gli operatori sono visivamente che indica il modo di trasferimento, essi sono destinati a fare di I / O, è non abusarne, è da libreria standard e cosa pratica. Basta guardare "cin >> qualcosa", cosa va dove? Da cin, a qualcosa, ovviamente.
peenut,

7
@peenut: Ma il loro uso originale era un po 'mutevole. La "libreria standard" utilizza l'operatore in un modo che confonde completamente con la definizione originale.
Joe Z.

1
Sono sicuro di aver letto da qualche parte che Bjarne Stroustrup (creatore di C ++) ha sperimentato l'utilizzo al =posto di <<e >>nei primi giorni del C ++, ma ha riscontrato problemi in quanto non aveva la giusta precedenza per l'operatore (ovvero cerca prima argomenti a sinistra o a destra). Quindi le sue mani erano un po 'legate a ciò che poteva usare.
Phil Wright,

8

In generale non è una brutta cosa.
Nuove lingue come C # hanno anche un sovraccarico dell'operatore.

È l'abuso del sovraccarico dell'operatore che è una cosa negativa.

Ma ci sono anche problemi con il sovraccarico dell'operatore come definito in C ++. Poiché gli operatori sovraccarichi sono solo zucchero sintattico per le chiamate di metodo, si comportano proprio come il metodo. D'altra parte i normali operatori integrati non si comportano come i metodi. Queste incoerenze possono causare problemi.

Fuori dalla cima dei miei operatori ||e &&.
Le versioni integrate di questi sono operatori di scelta rapida. Questo non è vero per le versioni sovraccariche e ha causato alcuni problemi.

Il fatto che + - * / restituiscano tutti lo stesso tipo su cui operano (dopo la promozione dell'operatore)
Le versioni sovraccaricate possono restituire qualsiasi cosa (è qui che si verifica l'abuso, se i tuoi operatori iniziano a restituire un tipo di arbitro che l'utente non si aspettava le cose vanno giù per la collina).


8

Il sovraccarico dell'operatore non è qualcosa di cui "hai bisogno" molto spesso, ma quando usi Java, se colpisci un punto in cui ne hai davvero bisogno, ti farà venire voglia di strapparti le unghie solo così hai una scusa per smettere di scrivere .

Quel codice che hai appena trovato trabocca a lungo? Sì, dovrai riscrivere l'intero lotto per farlo funzionare con BigInteger. Non c'è niente di più frustrante che dover reinventare la ruota solo per cambiare il tipo di una variabile.


6

Guy Steele ha sostenuto che il sovraccarico dell'operatore dovrebbe essere anche in Java, nel suo discorso principale "Crescere una lingua" - c'è un video e una sua trascrizione, ed è davvero un discorso fantastico. Ti chiederai di cosa sta parlando per le prime due pagine, ma se continui a leggere, vedrai il punto e raggiungerai l'illuminazione. E anche il fatto stesso di poter fare un discorso del genere è sorprendente.

Allo stesso tempo, questo discorso ha ispirato molte ricerche fondamentali, tra cui probabilmente Scala - è uno di quei documenti che tutti dovrebbero leggere per lavorare sul campo.

Tornando al punto, i suoi esempi riguardano principalmente le classi numeriche (come BigInteger e alcune cose più strane), ma questo non è essenziale.

È vero, tuttavia, che l'uso improprio del sovraccarico dell'operatore può portare a risultati terribili e che persino un uso corretto può complicare le cose, se si tenta di leggere il codice senza studiare un po 'le librerie che utilizza. Ma è una buona idea? OTOH, tali librerie non dovrebbero cercare di includere un cheat sheet per gli operatori?


4

Credo che OGNI risposta abbia perso questo. In C ++ puoi sovraccaricare gli operatori come vuoi, ma non puoi influenzare la precedenza con cui vengono valutati. Scala non ha questo problema, IIRC.

Per quanto sia una cattiva idea, oltre alle questioni di precedenza, le persone escogitano significati davvero stupidi per gli operatori e raramente aiuta la leggibilità. Le biblioteche Scala sono particolarmente cattive per questo, simboli sciocchi che devi memorizzare ogni volta, con i manutentori delle biblioteche che attaccano le loro teste nella sabbia dicendo: "devi solo impararlo una volta". Bene, ora ho bisogno di imparare una sintassi criptica dell'autore "intelligente" * il numero di librerie che mi interessa usare. Non sarebbe così male se esistesse una convenzione di SEMPRE che fornisce una versione alfabetica degli operatori.


1
Scala ha anche una precedenza fissa per l'operatore, vero?
Skaffman,

Credo che ci sia, ma è molto più piatto. Più precisamente, Scala ha meno periodi per gli operatori. +, -, * sono metodi, non operatori, IIRC. Ecco perché 2 + 3 * 2, non è 8, è 10.
Saem

7
Scala ha un sistema di precedenza basato sul primo carattere del simbolo. scala> 2 + 3 * 2 res0: Int = 8
James Iry

3

Il sovraccarico dell'operatore non era un'invenzione del C ++: proveniva dall'Algol IIRC e persino Gosling non sostiene che sia una cattiva idea in generale.


Certo, ma fu nella sua incarnazione in C ++ che ottenne un'aria generale di mancanza di reputazione.
Skaffman,

5
Che cosa intendi con "aria generale di mancanza di reputazione"? Molte persone che conosco usano linguaggi che supportano il sovraccarico dell'operatore (C ++, C #) e non ho mai sentito lamentele.
Nemanja Trifunovic,

Sto parlando della mia lunga esperienza con C ++ pre-ANSI, e certamente ricordo una comune avversione per loro. Forse la situazione è migliorata con ANSI C ++ o le persone hanno appena imparato a non abusarne.
Skaffman,

1
Parlando come qualcuno che usa il C ++ da giorni di facciata (metà degli anni '80), posso assicurarti che l'introduzione dello standard ISO non ha avuto alcun effetto sui pregiudizi delle persone in merito al sovraccarico degli operatori.

3

L'unica cosa conosciuta in C ++ è la mancanza della possibilità di sovraccaricare [] = come operatore separato. Questo potrebbe essere difficile da implementare in un compilatore C ++ per quello che probabilmente non è una ragione ovvia ma ne vale la pena.


2

Come hanno sottolineato le altre risposte; il sovraccarico dell'operatore non è necessariamente negativo. Cosa c'è di male quando viene utilizzato in modi che rendono ovvio il codice risultante. Generalmente quando li usi devi farli fare la cosa meno sorprendente (avere l'operatore + do divisione causerebbe problemi per l'uso di una classe razionale) o come dice Scott Meyers:

I clienti sanno già come si comportano tipi come int, quindi dovresti cercare di far sì che i tuoi tipi si comportino allo stesso modo ogni volta che è ragionevole ... In caso di dubbio, fai come fanno gli ints . (Da Effective C ++ 3rd Edition item 18)

Ora alcune persone hanno portato il sovraccarico dell'operatore all'estremo con cose come boost :: spirit . A questo livello non hai idea di come sia implementato, ma crea una sintassi interessante per ottenere ciò che desideri. Non sono sicuro che sia positivo o negativo. Sembra carino, ma non l'ho usato.


Non sto discutendo a favore o contro il sovraccarico dell'operatore qui, non sto cercando persone per giustificarli.
Skaffman,

Sprint non si avvicina affatto all'esempio peggiore che abbia mai visto: dovresti vedere a cosa serve la libreria del database RogueWave!

Sono d'accordo che Spirit abusa degli operatori, ma non riesco davvero a pensare a un modo migliore per farlo.
Zifre,

1
Non credo che lo spirito stia abusando abbastanza degli operatori, ma lo sta spingendo. Sono d'accordo che non c'è davvero nessun altro modo di farlo. Fondamentalmente crea un DSL all'interno della sintassi di C ++. Molto lontano da ciò che C ++ è stato progettato per fare. Sì, ci sono esempi molto peggiori :) In generale li uso dove appropriato. Principalmente solo operatori di streaming per un debug \ logging più semplice. E anche lì è solo lo zucchero che inoltra a un metodo implementato nella classe.
Matt Price

1
È una domanda gustativa; ma le librerie combinatrici parser, in linguaggi funzionali, sovraccaricano gli operatori in un modo molto simile allo Spirito, e nessuno discute contro questo. Ci sono molte ragioni tecniche per le quali sono migliori: Google per "linguaggi specifici del dominio incorporato" per trovare un sacco di articoli che lo spiegano da un punto di vista generale e Google per "combinatore scala parser" per esempi pratici in questo caso. È vero che nei linguaggi funzionali la sintassi risultante è spesso migliore - ad esempio, non è necessario modificare il significato di >> per concatenare i parser.
Blaisorblade,

2

Non ho mai visto un articolo in cui si afferma che il sovraccarico dell'operatore del C ++ è negativo.

Gli operatori definibili dall'utente consentono un livello più semplice di espressività e usabilità per gli utenti della lingua.


1

Tuttavia, non sembrerebbe essere qualitativamente diverso dal sovraccarico dell'operatore in C ++, dove, come ricordo, gli operatori sono definiti come funzioni speciali.

AFAIK, Non c'è nulla di speciale nelle funzioni dell'operatore rispetto alle funzioni "normali" dei membri. Ovviamente hai solo un certo set di operatori che puoi sovraccaricare, ma ciò non li rende molto speciali.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.