C ++ 'da yapılar karşılaştırılırken == operatörü bulunamadı


100

Aşağıdaki yapının iki örneğini karşılaştırdığımda bir hata alıyorum:

struct MyStruct1 {
    MyStruct1(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
        my_struct_2(_my_struct_2),
        an_int(_an_int)
    {}

    std::string toString() const;

    MyStruct2 my_struct_2;
    int an_int;
};

Hata şudur:

C2678 hatası: ikili '==': 'myproj :: MyStruct1' türünde bir sol işleneni alan operatör bulunamadı (veya kabul edilebilir bir dönüşüm yok)

Neden?

Yanıtlar:


130

C ++ 'da, structs varsayılan olarak oluşturulan bir karşılaştırma işlecine sahip değildir. Kendin yazmalısın:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}

21
@Jonathan: C ++ neden structeşitlik için s'nizi nasıl karşılaştırmak istediğinizi bilsin ? Ve basit yolu istiyorsanız, memcmpyapılarınız her zaman çok uzun süre işaretçi içermiyor.
Xeo

12
@Xeo: memcmpPOD olmayan üyelerde (benzer std::string) ve yastıklı yapılarda başarısız olur .
fredoverflow

16
@Jonathan Bildiğim "modern" diller bir ==operatör sağlıyor - neredeyse hiç istenmeyen bir anlambilim. (Ve bunu geçersiz kılmak için bir araç sağlamazlar, bu nedenle bir üye işlevi kullanmak zorunda kalırsınız). Bildiğim "modern" diller de değer anlambilim sağlamaz, bu nedenle uygun olmadıklarında bile işaretçiler kullanmak zorunda kalırsınız.
James Kanze

4
@Jonathan Cases, belirli bir program içinde bile kesinlikle değişiklik gösterir. Varlık nesneleri için Java tarafından sağlanan çözüm çok iyi çalışıyor (ve tabii ki C ++ 'da aynı şeyi yapabilirsiniz - varlık nesneleri için bile deyimsel C ++). Soru, değer nesneleri hakkında ne yapılacağıdır. C ++, operator=C uyumluluğundan ötürü bir varsayılan sağlar (çoğu zaman yanlış bir şey yapsa bile). Ancak C uyumluluğu bir gerektirmez operator==. Küresel olarak, Java'nın yaptığına C ++ ne yaptığını tercih ederim. (C # bilmiyorum, bu yüzden belki daha iyi.)
James Kanze

9
En azından mümkün olmalı= default !
user362515

98

C ++ 20 ,operator<=> derleyici tarafından oluşturulan </ <=/ ==/ !=/ >=/ ve / veya >işleçleri bariz / naif (?) Uygulama ile talep etmenize olanak tanıyan "uzay gemisi" olarak adlandırılan varsayılan karşılaştırmaları tanıttı ...

auto operator<=>(const MyClass&) const = default;

... ancak bunu daha karmaşık durumlar için özelleştirebilirsiniz (aşağıda tartışılmıştır). Gerekçeler ve tartışma içeren dil önerisi için buraya bakın . Bu cevap, C ++ 17 ve öncesi için ve operator<=>...

Bunu daha önce Standartlaştırmamış olmak C ++ 'ın biraz yararsız görünebilir, ancak genellikle yapıların / sınıfların karşılaştırmanın dışında tutacakları bazı veri üyeleri vardır (örn. Sayaçlar, önbelleğe alınan sonuçlar, konteyner kapasitesi, son işlem başarısı / hata kodu, imleçler) ve bunlarla sınırlı olmamak üzere sayısız şey hakkında alınacak kararlar :

  • ilk önce hangi alanların karşılaştırılacağı, örneğin belirli bir intüyeyi karşılaştırmak eşit olmayan nesnelerin% 99'unu çok hızlı bir şekilde ortadan kaldırabilirken, bir map<string,string>üye genellikle aynı girdilere sahip olabilir ve karşılaştırmak nispeten pahalı olabilir - değerler çalışma zamanında yüklenirse, programcı derleyici olamaz
  • dizeleri karşılaştırmada: büyük / küçük harf duyarlılığı, boşluk ve ayırıcıların denkliği, kaçış kuralları ...
  • kayan / çiftleri karşılaştırırken hassasiyet
  • NaN kayan nokta değerlerinin eşit kabul edilip edilmeyeceği
  • işaretçileri veya veriye işaret edenleri karşılaştırma (ve eğer ikincisi ise, işaretçilerin diziler için olup olmadığını ve karşılaştırmaya ihtiyaç duyan kaç nesne / bayt olduğunu nasıl anlarsınız)
  • Sıralanmamış kapları karşılaştırırken sıralamanın önemli olup olmadığı (örn vector. list) ve öyleyse, karşılaştırma yapmadan önce bunları yerinde sıralamanın uygun olup olmadığı, her karşılaştırma yapıldığında geçici değerleri sıralamak için ekstra bellek kullanmanın uygun olup olmadığı
  • Şu anda kaç dizi öğesi karşılaştırılması gereken geçerli değerleri tutuyor (bir yerde veya bir gözcü boyut var mı?)
  • unionkarşılaştırılacak bir üye
  • normalleştirme: örneğin, tarih türleri aralık dışı güne veya yılın ayına izin verebilir veya bir rasyonel / kesir nesnesi 6 / 8'ine sahipken diğerinde 3 / 4ers olabilir, bu performans nedenleriyle düzeltir ayrı bir normalleştirme adımı ile tembel olarak; karşılaştırmadan önce normalleşmeyi tetikleyip tetiklemeyeceğinize karar vermeniz gerekebilir
  • zayıf işaretçiler geçerli olmadığında ne yapmalı
  • operator==Kendilerini uygulamayan (ancak compare()veya operator<veya str()veya alıcılara sahip olabilen ...) üyeler ve üsler nasıl ele alınır
  • diğer iş parçacıklarının güncellemek isteyebileceği verileri okurken / karşılaştırırken hangi kilitler alınmalıdır?

Bu nedenle, karşılaştırmanın derlemesine izin vermek, ancak çalışma zamanında size anlamlı bir sonuç vermemesi yerine , belirli yapınız için ne anlama gelmesi gerektiğini açıkça düşünene kadar bir hata olması güzel .

C Eğer diyelim ++ eğer söyledi Bütün bunlar, iyi olurdu bool operator==() const = default;bir "naif" üye bazında üyesi karar vermiştim zaman ==testi oldu Tamam. Aynı !=. Verilen çoklu üyeleri / bazlar, "varsayılan" <, <=, >, ve >=uygulamalar umutsuz olsa görünüyor - beyanı olası ama, istediği göre gruplama, bazlar üyeleri önce mutlaka olmak (üye sipariş için zorunlulukları çelişkili verilen ne olduğunu düşünmek için pek sırasına göre basamaklı erişilebilirlik, yapım / yıkımdan önce bağımlı kullanım). Daha yaygın bir şekilde kullanışlı olması için, C ++, seçimleri yönlendirmek için yeni bir veri üyesine / temel açıklama sistemine ihtiyaç duyacaktır - bu, Standartta olması harika bir şey olurdu, ideal olarak AST tabanlı kullanıcı tanımlı kod oluşturma ile birleşti ... o'

Eşitlik operatörlerinin tipik uygulaması

Makul bir uygulama

Bu var olasılıkla makul ve etkili uygulama olacağını:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.my_struct2 == rhs.my_struct2 &&
           lhs.an_int     == rhs.an_int;
}

Bunun operator==için MyStruct2de gerekli olduğunu unutmayın .

Bu uygulamanın etkileri ve alternatifleri, aşağıdaki MyStruct1 cihazınızın özelliklerinin tartışılması başlığı altında tartışılmaktadır .

==, <,> <= vb. İçin tutarlı bir yaklaşım

std::tupleKendi sınıf örneklerinizi karşılaştırmak için karşılaştırma işleçlerinden yararlanmak kolaydır - sadece std::tieistenen karşılaştırma sırasına göre alanlara referans demetleri oluşturmak için kullanın . Örneğimi buradan genellemek :

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return std::tie(lhs.my_struct2, lhs.an_int) ==
           std::tie(rhs.my_struct2, rhs.an_int);
}

inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return std::tie(lhs.my_struct2, lhs.an_int) <
           std::tie(rhs.my_struct2, rhs.an_int);
}

// ...etc...

Karşılaştırmak istediğiniz sınıfa "sahip olduğunuzda" (yani, kurumsal ve üçüncü taraf kitaplarla bir faktör düzenleyebildiğinizde) ve özellikle C ++ 14'ün ifadeden işlev dönüş türünü çıkarmaya hazır olması durumunda return, genellikle bir " "üye işlevini, karşılaştırabilmek istediğiniz sınıfa bağlayın:

auto tie() const { return std::tie(my_struct1, an_int); }

Daha sonra yukarıdaki karşılaştırmalar şu şekilde basitleşir:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.tie() == rhs.tie();
}

Daha kapsamlı bir karşılaştırma operatörü seti istiyorsanız, operatörleri artırmanızı öneririm (arayın less_than_comparable). Herhangi bir nedenle uygun değilse, destek makroları fikrini beğenebilir veya beğenmeyebilirsiniz (çevrimiçi) :

#define TIED_OP(STRUCT, OP, GET_FIELDS) \
    inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
    { \
        return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
    }

#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
    TIED_OP(STRUCT, ==, GET_FIELDS) \
    TIED_OP(STRUCT, !=, GET_FIELDS) \
    TIED_OP(STRUCT, <, GET_FIELDS) \
    TIED_OP(STRUCT, <=, GET_FIELDS) \
    TIED_OP(STRUCT, >=, GET_FIELDS) \
    TIED_OP(STRUCT, >, GET_FIELDS)

... bu daha sonra bir la kullanılabilir ...

#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)

(C ++ 14 üye bağlı sürümü burada )

MyStruct1 cihazınızın özelliklerinin tartışılması

Bağımsız üyeye karşı bağımsız olma seçiminin çıkarımları vardır operator==()...

Bağımsız uygulama

İlginç bir karar vermen gerekiyor. Sınıfınız dolaylı olarak a'dan oluşturulabildiğinden, MyStruct2bağımsız / üye olmayan bir bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)işlev ...

my_MyStruct2 == my_MyStruct1

... önce geçici bir MyStruct1kaynak oluşturup my_myStruct2sonra karşılaştırma yaparak. Bu, kesinlikle kurucunun MyStruct1::an_intvarsayılan parametre değerine set bırakacaktır -1. Eğer içerip içermediğine bağlı olarak an_intsizin uygulanmasında karşılaştırma operator==, bir MyStruct1kudretini veya eşit karşılaştırmak olmayabilir MyStruct2kendisi eşit karşılaştırır MyStruct1'ın my_struct_2üyesi! Dahası, bir geçici oluşturmak MyStruct1çok verimsiz bir işlem olabilir, çünkü mevcut my_struct2üyeyi geçiciye kopyalamayı içerir , sadece karşılaştırmadan sonra onu atmak için. (Elbette, bu yapıcıyı MyStruct1yaparak explicitveya için varsayılan değeri kaldırarak karşılaştırma için bu örtük yapılandırmayı engelleyebilirsiniz an_int.)

Üye uygulaması

A ' MyStruct1dan a' nın örtük olarak oluşturulmasından kaçınmak istiyorsanız MyStruct2, karşılaştırma işlecini bir üye işlevi yapın:

struct MyStruct1
{
    ...
    bool operator==(const MyStruct1& rhs) const
    {
        return tie() == rhs.tie(); // or another approach as above
    }
};

Not consttek üyesi uygulanması için gerekli - - anahtar kelime nesneleri karşılaştırarak bunları değiştirmek olmadığını derleyici bildirir, böylece izin verilebilir constnesneler.

Görünür temsillerin karşılaştırılması

Bazen istediğiniz türden bir karşılaştırma yapmanın en kolay yolu ...

    return lhs.to_string() == rhs.to_string();

... ki bu da genellikle çok pahalıdır - bunlar stringacı bir şekilde atılmak için yaratılmıştır! Kayan nokta değerleri olan türler için, görünür temsillerin karşılaştırılması, görüntülenen basamak sayısının, karşılaştırma sırasında neredeyse eşit değerlerin eşit olarak değerlendirildiği toleransı belirlediği anlamına gelir.


Aslında, karşılaştırma operatörleri için <,>, <=,> = yalnızca <uygulamak gerekir. Gerisi takip eder ve otomatik olarak üretilebilen uygulamadan farklı bir anlam ifade eden bunları uygulamanın anlamlı bir yolu yoktur. Hepsini kendi başınıza uygulamak zorunda olmanız tuhaf.
André

@ Andre: daha sık elle yazılmış int cmp(x, y)veya comparefonksiyonu için negatif bir değer iade x < yeşitliği, 0 ve pozitif bir değer x > yiçin temel olarak kullanılan <, >, <=, >=, ==, ve !=; Tüm bu operatörleri bir sınıfa enjekte etmek için CRTP'yi kullanmak çok kolaydır. Eminim uygulamayı eski bir cevapta yayınladım, ancak hızlı bir şekilde bulamadım.
Tony Delroy

@TonyD Tabii bunu yapabilir, ama sadece kadar kolay uygulamaktır >, <=ve >=bakımından <. Sen olabilir ayrıca uygulamak ==ve !=bu şekilde, ama bu genellikle sanırım çok etkili bir uygulama olmaz. Tüm bunlar için CRTP veya başka numaralara ihtiyaç duyulmasaydı güzel olurdu, ancak standart, kullanıcı tarafından açıkça tanımlanmamışsa ve tanımlanmışsa bu operatörlerin otomatik olarak oluşturulmasını zorunlu kılacaktır <.
André

André @: bunun nedeni var ==ve !=verimli bir şekilde kullanılarak ifade olabilir <her şey yaygındır için karşılaştırmak kullanarak söyledi. "Hiçbir CRTP veya diğer hileler gerekli olsaydı iyi olurdu" - belki, ama sonra CRTP kolayca diğer operatörlerin çok (üretmek için kullanılabilir örneğin bit düzeyinde |, &, ^dan |=, &=ve ^=; + - * / %onların atama formlardan; ikili -tekli olumsuzlama gelen ve +) - bu temanın o kadar çok potansiyel olarak yararlı varyasyonu ki, sadece oldukça gelişigüzel bir dilim için bir dil özelliği sağlamak pek de zarif değil.
Tony Delroy

Mantıklı bir uygulamayastd::tie birden çok üyenin karşılaştırmasını yapmak için kullanılan bir sürümü eklemenin bir sakıncası var mı?
NathanOliver

17

Açıkça tanımlamanız gerekir operator ==için MyStruct1.

struct MyStruct1 {
  bool operator == (const MyStruct1 &rhs) const
  { /* your logic for comparision between "*this" and "rhs" */ }
};

Şimdi == karşılaştırması bu tür 2 nesne için yasaldır.


11

C ++ 20'de başlayarak, varsayılan karşılaştırma operatörleri (tam kümesi eklemek mümkün olmalıdır ==, <=bir bildirerek bir sınıfa, vb) varsayılan üç yönlü karşılaştırma operatörü böyle, ( "uzay gemisi" operatör):

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

Uyumlu bir C ++ 20 derleyicisiyle, bu satırı MyStruct1 ve MyStruct2'ye eklemek, MyStruct2 tanımının uyumlu olduğunu varsayarak eşitlik karşılaştırmalarına izin vermek için yeterli olabilir.


2

Karşılaştırma, C veya C ++ 'daki yapılarda çalışmaz. Bunun yerine alanlara göre karşılaştırın.


2

Varsayılan olarak yapıların bir ==operatörü yoktur. Kendi uygulamanızı yazmanız gerekecek:

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }

0

Kutunun dışında, == operatörü yalnızca ilkeller için çalışır. Kodunuzun çalışması için, yapınız için == operatörünü aşırı yüklemeniz gerekir.


0

Çünkü yapınız için bir karşılaştırma operatörü yazmadınız. Derleyici bunu sizin için üretmez, bu nedenle karşılaştırma istiyorsanız, kendiniz yazmalısınız.

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.