Eşitsizlik neden birçok C ++ standart kütüphane kodunda (! (A == b)) olarak test ediliyor?


142

Bu, C ++ standart kitaplık removekodundaki koddur. Eşitsizlik neden if (!(*first == val))yerine test ediliyor if (*first != val)?

 template <class ForwardIterator, class T>
      ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val)
 {
     ForwardIterator result = first;
     while (first!=last) {
         if (!(*first == val)) {
             *result = *first;
             ++result;
         }
         ++first;
     }
     return result;
 }

2
@BeyelerStudios muhtemelen doğrudur. Uygulama yaparken de yaygındır operator!=. Sadece operator==uygulamayı kullanın :bool operator!=(const Foo& other) { return !(*this == other); }
simon

1
aslında operator==
ifademi

Oh, ve constönceki yorumumda da bir örnek olmalı , ama anladın. (Düzenlemek için çok geç)
simon

Bunun nedeni ilgilidir başka bir soru ve varlık kavramı (yani temelde "ille No" ile cevap olabilir) EqualityComparableHurkyl belirtilen bu onun cevabını .
Marco13

Yanıtlar:


144

Çünkü bu T için tek gereksinimin bir operator==. T'ye sahip olmanız gerekebilir, operator!=ancak burada genel fikir, şablonun kullanıcısına mümkün olduğunca az yük bindirmeniz ve diğer şablonların ihtiyaç duymasıdır operator==.


13
şablon <sınıf T> satır içi bool operatörü! = <T a, Tb> {dönüş! (a == b); }
Joshua

8
Bir derleyicinin tüm örneklerini değiştiremeyeceği bir senaryo var mı? (==)? Bu derleyicinin yapması gereken varsayılan işlem neden olmasın?
Aidan Gomez

20
@AidanGomez Daha iyi veya daha kötü için operatörleri istediğiniz gibi yapmak için aşırı yükleyebilirsiniz. Mantıklı olması veya tutarlı olması gerekmez.
Neil Kirk

10
x != yile aynı olarak tanımlanmamıştır !(x == y). Bu operatörler gömülü DSL'nin ayrıştırma ağacını döndürürse ne olur?
Brice M. Dempsey

7
@Joshua !=Desteklenip desteklenmediğini tespit etmek için SFINAE kullanılmaya çalışılırsa kötü bir şekilde kırılır (desteklenmese bile yanlış olarak doğru dönecektir operator==!). Ayrıca, bazı kullanımlarının !=belirsiz olmasına neden olacağından endişe duyuyorum .

36

STL'deki çoğu işlev yalnızca operator<veya ile çalışır operator==. Bu, kullanıcının yalnızca bu iki operatörü (veya bazen bunlardan en az birini) uygulamasını gerektirir. Örneğin std::set, siparişleri yönetmek için değil operator<(daha kesin olarak varsayılan olarak std::lessçağıran operator<) kullanır operator>. removeSizin örnekte şablon benzer bir durumdur - bu sadece kullanan operator==değil operator!=bu yüzden operator!=tanımlanması gerekmez.


2
İşlevler operator<doğrudan kullanılmaz std::less, bunun yerine varsayılan olarak kullanılır operator<.
Christian Hackl

1
Aslında, standart algoritma işlevlerinin, örneğin aksine std::set, operator<doğrudan kullandıkları görülmektedir . Garip ...
Christian Hackl

1
Bu işlevler kullanmaz std::equal_to, operator==soruda belirtildiği gibi kullanırlar . Durum std::lessbenzer. Belki std::setde en iyi örnek değil.
Lukáš Bednařík

2
@ChristianHackl std::equal_tove std::lesskarşılaştırıcının parametre olarak alındığı varsayılan şablon parametreleri olarak kullanılır. operator==ve operator<örneğin, yineleyiciler ve rasgele erişimli yineleyiciler gibi eşitliği karşılaştırılabilir ve katı zayıf sıralamayı karşılamak için doğrudan tipin gerekli olduğu yerlerde kullanılır.
Jan Hudec

28

Bu kod, C ++ standart kitaplığı kaldırma kodudur.

Yanlış. O değil C ++ standart kütüphanesi kodu. Bu var bir olası iç uygulama C ++ standart kütüphane fonksiyonu. C ++ standardı gerçek kodu yazmaz; fonksiyon prototipleri ve gerekli davranışları öngörür.removeremove

Başka bir deyişle: Katı bir dil açısından gördüğünüz kod mevcut değildir . Derleyicinizin standart kitaplık uygulamasıyla birlikte gelen bazı başlık dosyasından olabilir. C ++ standardının bu başlık dosyalarının varlığını bile gerektirmediğini unutmayın . Dosyalar, derleyici uygulayıcılarının bir satırın gereksinimlerini karşılaması için uygun bir yoldur #include <algorithm>(örn. std::removeVe diğer işlevler kullanılabilir).

Eşitsizlik neden if (!(*first == val))yerine test ediliyor if (*first != val)?

Çünkü sadece operator==fonksiyon gerekli.

Özel türler için operatör aşırı yüklenmesi söz konusu olduğunda, dil her türlü garip şeyi yapmanıza izin verir. Aşırı yüklenmiş operator==ancak aşırı yüklü olmayan bir sınıf oluşturabilirsiniz operator!=. Ya da daha da kötüsü: Aşırı yükleyebilirsiniz, operator!=ancak tamamen ilgisiz şeyler yapmasını sağlayabilirsiniz .

Bu örneği düşünün:

#include <algorithm>
#include <vector>

struct Example
{
    int i;

    Example() : i(0) {}

    bool operator==(Example const& other) const
    {
        return i == other.i;
    }

    bool operator!=(Example const& other) const
    {
        return i == 5; // weird, but nothing stops you
                       // from doing so
    }

};

int main()
{
  std::vector<Example> v(10);
  // ...
  auto it = std::remove(v.begin(), v.end(), Example());
  // ...
}

Eğer std::removekullanılan operator!=, sonuç çok farklı olurdu.


1
Başka bir şey düşünmek her ikisi için mümkün olabileceğidir a==bve a!=breturn false için. Böyle bir durumun daha anlamlı olarak "eşit" veya "eşit değil" olarak kabul edilip edilmeyeceği her zaman açık olmasa da, eşitliği yalnızca "==" operatörü olarak tanımlayan bir işlev bunları "eşit olmayan" olarak kabul etmelidir ", hangi davranışın daha mantıklı olacağına bakılmaksızın [druthers'ım olsaydı, tüm türlerin boolean-getirmesi beklenirdi" == "ve"! = "operatörleri tutarlı bir şekilde davranırlar, ancak IEEE-754'ün eşitsizliği zorunlu kılması gerçeği operatörler böyle bir beklentiyi zorlaştırır].
supercat

18
"Kesin bir dil bakış açısından, gördüğünüz kod mevcut değil." - bir bakış açısı bir şeyin var olmadığını söylediğinde, ama aslında o şeye bakıyorsunuz, o zaman bakış açısı yanlıştır. Aslında standart kodun mevcut olmadığını söylemiyor, sadece var olduğunu söylemiyor :-)
Steve Jessop

@SteveJessop: "Katı bir dil bakış açısından" ifadesini "kesinlikle, dil düzeyinde" gibi bir ifadeyle değiştirebilirsiniz. Mesele şu ki, OP tarafından gönderilen kod dil standardının endişesi değildir.
Christian Hackl

2
@supercat: IEEE-754 her zaman en az bir işlenenin ne zaman altı ilişkinin de değerlendirilmesi gerektiğini düşünmüş olsam da tutarlı bir şekilde davranıyor ==ve !=davranıyor . falseNaN
Ben Voigt

@BenVoigt: Ah, doğru. İki operatörün birbiri ile tutarlı olacak şekilde aynı kırık şekilde davranmasını sağlar, ancak yine de denklikle ilişkili diğer tüm normal aksiyomları ihlal etmeyi başarır (örneğin ne a == a ne de operasyonların garantisini desteklemezler) eşit değerlerde eşit sonuç verir).
supercat

15

Burada bazı iyi cevaplar. Sadece küçük bir not eklemek istedim.

Tüm iyi kütüphaneler gibi, standart kütüphane (en azından) iki çok önemli prensip göz önünde bulundurularak tasarlanmıştır:

  1. Kütüphanenizin kullanıcılarına kurtarabileceğiniz en az sorumluluğu koyun. Bunun bir kısmı, arayüzünüzü kullanırken onlara yapılacak en az işi vermekle ilgilidir. (kaçabildiğiniz kadar az operatör tanımlamak gibi). Diğer kısmı, onları şaşırtmamak ya da hata kodlarını kontrol etmelerini istemekle ilgilidir (bu yüzden arayüzleri tutarlı tutun ve <stdexcept>işler yanlış gittiğinde istisnalar atın ).

  2. Tüm mantıksal artıklığı ortadan kaldırın . Tüm karşılaştırmalar sadece bundan çıkarılabilir operator<, o zaman neden kullanıcıların başkalarını tanımlamasını talep etmelisiniz? Örneğin:

    (a> b) (b <a) 'ya eşittir

    (a> = b)! (a <b) 'ye eşittir

    (a == b)! ((a <b) || (b <a)) ile eşdeğerdir

    ve bunun gibi.

    Bu not Tabii ki, bir neden sorabiliriz unordered_mapgerektirir operator==ziyade (en azından varsayılan olarak) operator<. Cevap şudur ki, bir karma tabloda şimdiye kadar ihtiyacımız olan tek karşılaştırma eşitlik içindir. Dolayısıyla, bir eşitlik operatörü tanımlamalarını istemek daha mantıklı olarak tutarlıdır (yani kütüphane kullanıcısı için daha mantıklıdır). Birine operator<ihtiyaç duymak kafa karıştırıcı olacaktır, çünkü neden ona ihtiyaç duyacağınız hemen belli olmaz.


10
İkinci noktanıza ilişkin olarak: Mantıksal bir düzen olmasa bile, eşitlik açısından mantıksal olarak karşılaştırılabilecek veya bir düzen oluşturmanın çok yapay olacağı türler vardır. Örneğin, kırmızı kırmızı ve kırmızı yeşil değil, kırmızı doğal olarak yeşilden daha az mıdır?
Christian Hackl

1
Kesinlikle katılmak. Mantıksal bir sipariş olmadığı için bu öğeler sıralı bir kapta saklanmaz . Daha uygun şekilde operator==(ve hash) gerektiren sıralanmamış bir kapta saklanabilirler .
Richard Hodges

3. Mümkün olan en az standart operatörleri aşırı yükleyin . Bu, uyguladıkları başka bir nedendir !(a==b). Operatörlerin yansıtılmayan aşırı yüklenmesi, C ++ programının tamamen dağınık hale gelmesine neden olabileceğinden (artı, programcının çıldırmasını sağlayın çünkü kodunda hata ayıklama bir görev imkansız olabilir, çünkü belirli bir hatanın suçlusu bir odyssey'i andırıyor).
sentaksör

!((a < b) || (b < a))daha az bool operatörü kullanır, bu yüzden muhtemelen daha hızlıdır
Filip Haglund

1
Gerçekte değil. Montaj dilinde tüm karşılaştırmalar çıkarma olarak yapılır ve ardından bayrak kaydında taşıma ve sıfır bitleri test edilir. Diğer her şey sadece sözdizimsel şekerdir.
Richard Hodges

8

EqualityComparableKavram sadece gerektirir operator==tanımlanabilir.

Sonuç olarak, herhangi bir fonksiyon türleri tatmin edici ile çalışma professes bu EqualityComparable olamaz varlığına bağlıdır operator!=bu tür nesneler için. (varlığını ima eden ek gereksinimler yoksa operator!=).


1

En ümit verici yaklaşım, operatörün == belirli bir tür için çağrılıp çağrılmayacağını belirlemeye yönelik bir yöntem bulmak ve daha sonra yalnızca mevcut olduğunda bunu desteklemek; diğer durumlarda bir istisna atılır. Bununla birlikte, bugüne kadar f == g isteğe bağlı bir operatör ifadesinin uygun bir şekilde tanımlanıp tanımlanmadığını saptamanın bilinen bir yolu yoktur. Bilinen en iyi çözüm aşağıdaki istenmeyen özelliklere sahiptir:

  • Operatör == erişilemediği nesneler için derleme zamanında başarısız olur (örn. Özel olduğu için).
  • == çağrısı belirsizse derleme zamanında başarısız olur.
  • Operatör == derlemesi doğru olmasa bile, operatör == bildirimi doğruysa doğru görünüyor.

Boost SSS'den: kaynak

==Uygulama gerektirmenin bir yük olduğunu bilerek, uygulama gerektirerek asla ek yük oluşturmak istemezsiniz !=.

Şahsen benim için bu, SOLID (nesne yönelimli tasarım) L bölümü - Liskov ikame ilkesi: “bir programdaki nesneler, o programın doğruluğunu değiştirmeden alt türlerinin örnekleriyle değiştirilmelidir.” Bu durumda ben == ve boole mantığında boole tersi ile değiştirebileceğim operatör ! = .

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.