Scala'nın operatörünün aşırı yüklenmesini “iyi”, ancak C ++ 'ın “kötü” yapan özelliği nedir?


155

C ++ 'da operatör aşırı yüklenmesi, birçok kişi tarafından Kötü Bir Şey (tm) ve daha yeni dillerde tekrarlanmaması için bir hata olarak kabul edilir. Kesinlikle, Java tasarlanırken özellikle bırakılan bir özellikti.

Şimdi Scala'da okumaya başladım, operatör aşırı yüklemesine çok benzeyen bir şey olduğunu görüyorum (teknik olarak operatör aşırı yüklemesi olmamasına rağmen operatörleri yok, sadece fonksiyonları var). Ancak, hatırladığım gibi, özel fonksiyonlar olarak tanımlandığım C ++ 'da aşırı yüklenen operatörden niteliksel olarak farklı görünmüyor.

Benim sorum, Scala'da "+" tanımlamayı C ++ 'da olduğundan daha iyi bir fikir yapan şey nedir?


27
Her iki programcı arasında da Scala değil C ++ evrensel fikir birliği ile tanımlanmadı. Bazı insanların C ++ 'dan çıktığı gerçeği ile bazı insanların Scala' dan çıktığı gerçeği arasında herhangi bir çelişki olduğunu düşünmüyorum.
Steve Jessop

16
C ++ 'da operatör aşırı yüklemesi hakkında kötü bir şey yoktur.
köpek yavrusu

5
Bu yeni bir şey değil ama operatör aşırı yükleme ve diğer "gelişmiş" özellikleri söz konusu olduğunda C ++ savunmak yolu basit: C ++ bize uygun gördüğümüz gibi kullanmak / kötüye tüm gücü verir. Her zaman yetkin ve özerk olduğumuzu nasıl düşündüğümüzü beğendim ve bizim için böyle kararlara ihtiyacımız yok.
Elliott

Scala, c ++ 'dan on yıllar sonra tasarlanmıştı. Arkasındaki kişiyi programlama dilleri açısından süper zevkli buluyor. Ya kendiniz hakkında kötü bir şey yok, c ++ veya Scala'ya 100 yıl daha yapışırsanız, muhtemelen her ikisinin de kötü olduğu ortaya çıkıyor! Önyargılı olmak doğamızda var ama onunla savaşabiliriz, sadece teknoloji tarihine bakabiliriz, her şey modası geçmiş olur.
Nader Ghanbari

Yanıtlar:


242

C ++, C'den gerçek mavi operatörleri miras alır. Yani 6 + 4'teki "+" çok özeldir. Örneğin, bu + işlevine bir işaretçi alamazsınız.

Öte yandan Scala'nın bu şekilde operatörleri yok. Yöntem adlarını tanımlamada büyük bir esnekliğe ve ayrıca kelime olmayan semboller için yerleşik önceliğe sahiptir. Yani teknik olarak Scala'da operatör aşırı yüklenmesi yok.

Her ne demek isterseniz, operatör aşırı yüklenmesi C ++ 'da bile doğal olarak kötü değildir. Sorun, kötü programcıların kötüye kullanmasıdır. Ama açıkçası, programcıların operatör aşırı yüklemesini kötüye kullanma yeteneğini ortadan kaldırmanın, programcıların kötüye kullanabileceği tüm şeyleri düzeltme kovasına bir damla bırakmadığını düşünüyorum. Asıl cevap mentorluk. http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html

Bununla birlikte, C ++ 'ın operatör aşırı yüklenmesi ile Scala'nın esnek yöntem adlandırma arasında, IMHO, Scala'yı hem daha az istismar edilebilir hem de daha fazla istismar edilebilir kılan farklılıklar vardır.

C ++ 'da düzeltilmiş gösterim almanın tek yolu işleçleri kullanmaktır. Aksi takdirde object.message (argüman) veya pointer-> messsage (argüman) veya fonksiyon (argüman1, argüman2) kullanmalısınız. Kodunuza belirli bir DSLish tarzı istiyorsanız, operatörleri kullanmak için baskı var.

Scala'da herhangi bir mesaj gönderildiğinde infix gösterimi alabilirsiniz. "nesne mesajı argümanı" mükemmel bir şekilde tamamdır, yani sadece infix gösterimi için kelime dışı semboller kullanmanız gerekmez.

C ++ operatörünün aşırı yüklenmesi, esas olarak C operatörleriyle sınırlıdır. Sadece operatörlerin "ilgisiz" kavramları "+" ve ">>" gibi göreceli olarak az sayıda sembolle eşleştirmeye çalışmak için baskı uygulayan sınırlama ile birlikte

Scala, yöntem adları olarak çok çeşitli geçerli sözcük olmayan sembollere izin verir. Örneğin, yazabileceğiniz gömülü bir Prolog-ish DSL'im var

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

: -,!,? Ve & sembolleri sıradan yöntemler olarak tanımlanır. Yalnızca C ++ 'da geçerlidir ve bu DSL'i C ++ ile eşleme girişimi zaten çok farklı kavramları çağrıştıran bazı semboller gerektirir.

Tabii ki, bu aynı zamanda Scala'yı başka bir istismara da açar. Scala'da isterseniz $! & ^% Yöntemini adlandırabilirsiniz.

Scala gibi, sözcük olmayan işlev ve yöntem adlarının kullanımında esnek olan diğer diller için, Scala gibi her "operatörün" sadece başka bir yöntem olduğu Hastal'ın ve programcının esnek olarak adlandırılmış önceliği ve sabitliğini tanımlamasına izin veren Smalltalk'a bakın. fonksiyonlar.


Son kontrol ettim, 3.operator + (5) çalıştı. & (3.operator +) 'nın olmamasına gerçekten şaşırdım.
Joshua

örneğin c ++ 'da assert (female ("jane")) yapabilirsiniz. Bu hiç de kafa karıştırıcı olmaz - operatörün + kötü bir şey olmadığı ile ilgili james-iry mesajına geri dönün, ama aptal programcılar.
pm100

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

Bu bir grup kibirli saçmalık: "operatör aşırı yüklenmesi, C ++ 'da bile doğal olarak kötü değil. Sorun, kötü programcıların kötüye kullanmasıdır." Bir şey onu kullanmanın oldukça az yararı ile kolayca kötüye kullanılabilirse, genel sonuç, kodunuzu koruyan bir sonraki adamın, kodunuzun tuhaf kısımlarını deşifre etmekte üretkenliğini kaybedeceğidir. Aksi halde: Çok bilgilendirici ve iyi yazılmış cevap.
Jukka Dahlbom

@JukkaDahlbom Akıllı işaretçilerin varlığı, avantajı tek başına büyütür. Ve sonra lambdas, kullanıcı tanımlı sayı türleri, aralık türleri var ...
Alexey Romanov

66

C ++ 'da operatör aşırı yüklenmesi birçok kişi tarafından Kötü Bir Şey (tm) olarak kabul edilir

Sadece cahil tarafından. C ++ gibi bir dilde kesinlikle gereklidir ve "saf" bir bakış açısıyla yola çıkmaya başlayan diğer dillerin, tasarımcıları ne kadar gerekli olduğunu öğrendikten sonra bunu ekledikleri dikkat çekicidir.


30
Aslında Neil ile hemfikirim. Değişkenleri / sabitleri / nesneleri / örnekleri cebirsel varlıklar olarak sunmak istiyorsanız ve insanların etkileşimlerini matematiksel bir şekilde anlamalarını istiyorsanız, programlama IMHO'nun işleyişi bu olmalıdır.
Massa

16
+1, C ++ 'da operatör aşırı yükleme iyidir. Örneğin, vektör matematiğini daha temiz hale getirir. Birçok C ++ özelliğinde olduğu gibi, gücü dikkatli kullanmalısınız.
John Smith

7
@Kristo Çünkü C ++, atanması ve kopyalanması gereken değerleri kullanır. Bunun üzerinde kontrol sahibi olmanız gerekir, bu nedenle belirli bir tür için atama operatörünü en azından belirtebilirsiniz.

7
@Kristo: C ++ 'ın bir amacı, kullanıcı tanımlı türlerin yerleşik türlerin yaptığı her şeyi yapmasına izin vermektir (örtük dönüşümler gibi bazı bağlamlarda farklı şekilde ele alınsalar da). 27 bitlik bir tam sayı uygulamak istiyorsanız, bunu yapabilirsiniz ve bunu kullanmak int gibi olacaktır. Operatör aşırı yüklenmesi olmadan, yerleşik tiplerle aynı sözdizimine sahip UDT'lerin kullanılması mümkün olmaz ve bu nedenle elde edilen dil bu anlamda "C ++ gibi" olmaz.
Steve Jessop

8
"bu şekilde delilik yatıyor" - daha da kötüsü, bu şekilde std :: vector <bool> yatıyor!
Steve Jessop

42

Operatör aşırı yüklemesinin evrensel olarak hiçbir zaman C ++ 'da kötü bir fikir olduğu düşünülmemiştir - sadece operatör aşırı yüklemesinin kötüye kullanılması kötü bir fikir olarak düşünülmüştür. Zaten daha ayrıntılı fonksiyon çağrıları ile simüle edilebildiğinden, bir dilde operatörün aşırı yüklenmesi gerekmez. Java'da operatör aşırı yüklenmesinden kaçınmak, Java'nın uygulanmasını ve spesifikasyonunu biraz daha basitleştirdi ve programcıları operatörleri kötüye kullanmamaya zorladı. Java topluluğunda operatör aşırı yüklenmesi ile ilgili bazı tartışmalar oldu.

Scala'da operatör aşırı yüklemesinin avantajları ve dezavantajları C ++ ile aynıdır - operatör aşırı yüklemesini uygun şekilde kullanırsanız daha doğal kod yazabilirsiniz - ve kullanmazsanız daha şifreli, gizlenmiş kod yazabilirsiniz.

FYI: İşleçler C ++ 'da özel işlevler olarak tanımlanmazlar, diğer işlevler gibi davranırlar - ad aramalarında, üye işlevleri olmaları gerekip gerekmediği ve iki şekilde çağrılabilmeleri gibi bazı farklılıklar olsa da: 1 ) operatör sözdizimi ve 2) operatör işlev kimliği sözdizimi.


"Bir dilde operatör aşırı yüklemeye gerçekten ihtiyaç duymaz çünkü zaten daha ayrıntılı fonksiyon çağrıları ile simüle edilebilirler." Bu mantık altında operatörlere bile ihtiyaç duyulmaz . Neden sadece kullanmıyorsunuz add(2, multiply(5, 3))?
Joe Z.

Daha çok kullanılan normal gösterimleri eşleştirme örneğidir. Matematikçileri ve fizikçileri düşünün, operatörlerin aşırı yüklenmesini çok daha kolay sağlayan bir C ++ kütüphanesini anlayabilir ve kullanabilirler. Denkleme programlama dilinden ziyade konsantre olmayı tercih ederler.
Phil Wright

19

Bu makale - " C ++ ve Java'nın Olumlu Mirası " - sorunuzu doğrudan yanıtlar.

"C ++ hem yığın ayırma hem de yığın ayırmaya sahiptir ve tüm durumları işlemek ve bellek sızıntılarına neden olmamak için operatörlerinizi aşırı yüklemelisiniz. Gerçekten zordur. Bununla birlikte Java'nın tek bir depolama ayırma mekanizması ve operatörün aşırı yüklenmesini önemsiz hale getiren bir çöp toplayıcısı vardır". ..

Java yanlışlıkla (yazara göre) C ++ 'da karmaşık olduğu için operatör aşırı yüklemesini atladı, ancak nedenini unuttum (veya Java için geçerli olmadığını fark etmedi).

Neyse ki, Scala gibi daha üst düzey diller geliştiricilere aynı JVM'de çalışırken seçenekler sunuyor.


14
Eckel, operatör aşırı yüklemesinin C ++ 'daki komplikasyonlar nedeniyle Java'dan atıldığı fikri için gördüğüm tek kaynak ve kaynağının ne olduğunu söylemiyor. Ben indirim. Söylediğim diğer tüm kaynaklar potansiyel kötüye kullanım nedeniyle terk edildi. Bkz. Gotw.ca/publications/c_family_interview.htm ve newt.com/wohler/articles/james-gosling-ramblings-1.html . Sadece "operatör aşırı yüklenmesi" için sayfa arayın.
James Iry

9

Operatörün aşırı yüklenmesi ile ilgili yanlış bir şey yoktur. Aslında, ile bir terslik var değil sayısal türleri için aşırı operatörü olan. (BigInteger ve BigDecimal kullanan bazı Java kodlarına bakın.)

Yine de C ++ özelliği kötüye kullanma geleneğine sahiptir. Sıkça atıfta bulunulan bir örnek, bit kaydırma işleçlerinin G / Ç yapmak için aşırı yüklenmiş olmasıdır.


<< ve >> operatörleri görsel olarak aktarım yolunu gösterir, G / Ç yapmak içindir , kötüye kullanım değildir, standart kütüphaneden ve pratik şeyden gelir. Sadece "cin >> bir şeye" bakın, nereye gidiyor? Cinten, bir şeye, belli ki.
peenut

7
@peenut: Ama orijinal kullanımları biraz değişiyordu. "Standart kütüphane", operatörü orijinal tanımıyla tamamen karışacak şekilde kullanır.
Joe Z.

1
Bjarne Stroustrup'un (C ++ yaratıcısı) =yerine <<ve >>C ++ 'ın ilk günlerinde kullanmayı denediği bir yerde okuduğumdan eminim , ancak doğru operatör önceliğine sahip olmadığı için problemlerle karşılaştı (yani, önce solda veya sağda argümanlar). Böylece elleri ne kullanabileceği konusunda biraz bağlıydı.
Phil Wright

8

Genel olarak kötü bir şey değildir.
C # gibi yeni diller de operatör aşırı yüklenmesine sahiptir.

Operatörün aşırı yüklenmesinin kötüye kullanılması kötü bir şeydir.

Ancak, C ++ 'da tanımlandığı gibi operatör aşırı yüklenmesi ile ilgili sorunlar da vardır. Aşırı yüklenmiş operatörler yöntem çağrıları için sadece sözdizimsel şeker olduğundan, aynı yöntem gibi davranırlar. Öte yandan normal yerleşik operatörler yöntem gibi davranmazlar. Bu tutarsızlık sorunlara neden olabilir.

Baş operatörlerimin üstünde ||ve &&.
Bunların yerleşik versiyonları kısa yol operatörleridir. Bu, aşırı yüklenmiş sürümler için geçerli değildir ve bazı sorunlara neden olmuştur.

+ - * / tümünün üzerinde çalıştıkları aynı türü döndürmesi (operatör tanıtımından sonra)
Aşırı yüklenmiş sürümler her şeyi döndürebilir (Bu, kötüye kullanımın devreye girdiği yer, Operatörleriniz bazı hakem türlerini iade etmeye başlarsa, kullanıcı beklemiyordu işler tepeden aşağı iner).


8

Operatör aşırı yüklemesi, gerçekten çok sık "ihtiyaç duyduğunuz" bir şey değildir, ancak Java kullanırken, gerçekten ihtiyacınız olan bir noktaya vurursanız, tırnaklarınızı yırtmak isteyeceksiniz, böylece yazmayı bırakmak için bir bahaneniz olur. .

Az önce bulduğun kod uzun süre taştı mı? Evet, BigInteger ile çalışması için her şeyi yeniden yazmanız gerekecek. Sadece bir değişkenin türünü değiştirmek için tekerleği yeniden icat etmenin daha sinir bozucu bir şey yoktur.


6

Guy Steele, operatör aşırı yüklemesinin Java'da da olması gerektiğini, "Dil büyütme" açılış konuşmasında - bir video ve bunun bir transkripsiyonu olduğunu ve gerçekten şaşırtıcı bir konuşma olduğunu savundu. İlk birkaç sayfa için neden bahsettiğini merak edeceksiniz, ancak okumaya devam ederseniz, noktayı görecek ve aydınlanmaya ulaşacaksınız. Ve böyle bir konuşma yapabilmesi de şaşırtıcı.

Aynı zamanda, bu konuşma muhtemelen Scala da dahil olmak üzere birçok temel araştırmaya ilham verdi - bu, herkesin sahada çalışmak için okuması gereken gazetelerden biridir.

Konuya geri dönersek, örnekleri çoğunlukla sayısal sınıflarla (BigInteger ve bazı garip şeyler gibi) ilgilidir, ancak bu gerekli değildir.

Bununla birlikte, operatörün aşırı yüklenmesinin yanlış kullanımının korkunç sonuçlara yol açabileceği ve kullandığı kütüphaneleri biraz incelemeden kodu okumaya çalışırsanız, uygun kullanımların bile işleri karmaşıklaştırabileceği doğrudur. Ama bu iyi bir fikir mi? OTOH, bu tür kütüphaneler operatörleri için bir operatör hile sayfası eklemeye çalışmamalıdır?


4

HER yanıtın bunu kaçırdığına inanıyorum. C ++ 'da operatörleri istediğiniz kadar aşırı yükleyebilirsiniz, ancak değerlendirildikleri önceliği etkileyemezsiniz. Scala'nın bu sorunu yok, IIRC.

Kötü bir fikir olduğu için, öncelik sorunlarının yanı sıra, insanlar operatörler için gerçekten anlamsız anlamlar bulurlar ve nadiren okunabilirliğe yardımcı olurlar. Scala kütüphaneleri bunun için özellikle kötüdür, her seferinde ezberlemeniz gereken aptal semboller, kütüphane görevlileri başlarını kuma yapıştırarak 'sadece bir kez öğrenmeniz gerekir' der. Harika, şimdi bazı 'akıllı' yazarların şifreli sözdizimini * kullanmayı umduğum kütüphane sayısını öğrenmem gerekiyor. Operatörlerin okuryazar bir versiyonunu sağlayan DAİMA konvansiyonu mevcut olsaydı o kadar da kötü olmazdı.


1
Scala da operatör önceliğini sabitledi, değil mi?
skaffman

İnanıyorum ama çok daha düz. Daha da önemlisi, Scala'nın daha az operatör dönemi var. +, -, * operatörler değil, IIRC yöntemleridir. Bu yüzden 2 + 3 * 2, 8 değil, 10.
Saem

7
Scala, sembolün ilk karakterine dayanan bir öncelik sistemine sahiptir. scala> 2 + 3 * 2 res0: Int = 8
James Iry

3

Operatör aşırı yüklenmesi bir C ++ buluşu değildi - Algol IIRC'den geldi ve Gosling bile bunun genel olarak kötü bir fikir olduğunu iddia etmiyor.


Elbette, ama C ++ enkarnasyonunda genel bir itibarsızlık havası kazandı.
skaffman

5
Ne demek "genel hoşnutsuzluk havası"? Tanıdığım çoğu kişi, operatörün aşırı yüklenmesini destekleyen diller kullanıyor (C ++, C #) ve hiç şikayet duymadım.
Nemanja Trifunovic

ANSI öncesi C ++ ile uzun yıllara dayanan deneyimimden bahsediyorum ve kesinlikle onlardan ortak bir hoşnutsuzluğu hatırlıyorum. Belki ANSI C ++ ile durum düzeldi, ya da insanlar bunu nasıl kötüye kullanamayacaklarını öğrendi.
skaffman

1
Cfront günlerden beri (80'lerin ortası) C ++ kullanan biri olarak konuşurken, ISO standardının tanıtımının, operatörlerin aşırı yüklenmesi ile ilgili insanların önyargıları üzerinde hiçbir etkisi olmadığını garanti edebilirim.

3

C ++ 'da yanlış bilinen tek şey, [] = ayrı bir operatör olarak aşırı yükleme yeteneğinin olmamasıdır. Muhtemelen bariz bir neden değil ama buna değer bir şey için bir C ++ derleyicisinde uygulamak zor olabilir.


2

Diğer cevapların işaret ettiği gibi; operatörün kendisini aşırı yüklemesi mutlaka kötü değildir. Ortaya çıkan kodu belli olmayan şekillerde kullanıldığında kötü olan nedir. Genellikle bunları kullanırken en az şaşırtıcı şeyleri yapmalısınız (operatör + bölüme sahip olmak rasyonel bir sınıfın kullanımı için sorun yaratabilir) veya Scott Meyers'ın dediği gibi:

Müşteriler int türlerinin nasıl davrandığını zaten biliyorlar, bu yüzden türlerinizin makul olduğunda aynı şekilde davranmasını sağlamaya çalışmalısınız ... Şüphe duyduğunuzda, ints gibi yapın . (Etkili C ++ 3. Baskıdan 18. madde)

Şimdi bazı insanlar, boost :: spirit gibi şeylerle operatörün aşırı yüklenmesine neden oldu . Bu seviyede nasıl uygulandığına dair hiçbir fikriniz yok, ancak istediğinizi elde etmek için ilginç bir sözdizimi yapıyor. Bunun iyi mi kötü mü olduğundan emin değilim. Güzel görünüyor, ama ben kullanmadım.


Burada operatör aşırı yüklenmesine karşı ya da buna karşı çıkmıyorum, insanların onları haklı çıkarmasını istemiyorum.
skaffman

Sprint karşılaştığım en kötü örneğe yakın bir yere gelmiyor - RogueWave veritabanı kütüphanesinin ne yaptığını görmelisin!

Ruh'un operatörleri kötüye kullandığına katılıyorum, ama bunu yapmanın daha iyi bir yolunu düşünemiyorum.
Zifre

1
Ruhun operatörleri kötüye kullandığını sanmıyorum, ama onu zorluyor. Bunu yapmanın başka bir yolu olmadığını kabul ediyorum. Temel olarak C ++ 'ın sözdizimi içinde bir DSL oluşturur. Çok C ++ yapmak için tasarlanmış ne uzakta. Evet, çok daha kötü örnekler var :) Genel olarak bunları uygun yerlerde kullanıyorum. Çoğunlukla daha kolay hata ayıklama \ günlük kaydı için akış operatörleri. Ve orada bile sınıfta uygulanan bir yönteme giden şeker.
Matt Price

1
Bu bir tat sorusu; ancak ayrıştırıcı birleştirici kütüphaneleri, işlevsel dillerde, operatörleri Ruh'a çok benzer şekilde aşırı yükler ve kimse buna karşı çıkmaz. Daha iyi oldukları birçok teknik neden var - genel bir bakış açısından açıklayan çok sayıda kağıt bulmak için "gömülü alana özel diller" için Google ve bu durumda pratik örnekler için "scala ayrıştırıcı birleştirici" için Google. İşlevsel dillerde ortaya çıkan sözdiziminin genellikle daha iyi olduğu doğrudur - örneğin, ayrıştırıcıları birleştirmek için >>'nin anlamını değiştirmeniz gerekmez.
Blaisorblade

2

C ++ 'ın operatör aşırı yüklemesinin kötü olduğunu iddia eden bir makale görmedim.

Kullanıcı tanımlı operatörler, dil kullanıcıları için daha yüksek düzeyde ifade ve kullanılabilirlik sağlar.


1

Ancak, hatırladığım gibi, özel fonksiyonlar olarak tanımlandığım C ++ 'da aşırı yüklenen operatörden niteliksel olarak farklı görünmüyor.

AFAIK, "Normal" üye işlevlere kıyasla operatör işlevlerinde özel bir şey yoktur. Tabii ki sadece aşırı yükleyebileceğiniz belirli bir operatör setiniz var, ancak bu onları çok özel yapmıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.