Waarom wordt ongelijkheid getest als (!(a==b)) in veel C++ standaardbibliotheekcode?

Dit is de code uit de C++ standaardbibliotheek removecode. Waarom wordt ongelijkheid getest als if (!(*first == val))in plaats van 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;
 }

Antwoord 1, autoriteit 100%

Omdat dit betekent dat de enige vereiste voor T is om een ​​operator==te implementeren. Je zou kunnen eisen dat T een operator!=heeft, maar het algemene idee hier is dat je de gebruiker van de sjabloon zo min mogelijk moet belasten en andere sjablonen hebben operator==.


Antwoord 2, autoriteit 25%

De meeste functies in STL werken alleen met operator<of operator==. Dit vereist dat de gebruiker alleen deze twee operators (of soms ten minste één van hen) implementeert. Bijvoorbeeld std::setgebruikt operator<(meer precies std::lessdie standaard operator<aanroept ) en niet operator>om de bestelling te beheren. Het remove-sjabloon in uw voorbeeld is een soortgelijk geval – het gebruikt alleen operator==en niet operator!=dus de operator!=hoeft niet te worden gedefinieerd.


Antwoord 3, autoriteit 19%

Dit is de code uit de verwijdercode van de C++-standaardbibliotheek.

Fout. Het is niet deC++ standaard bibliotheek removecode. Het is een mogelijke interne implementatievan de C++-standaardbibliotheek remove-functie. De C++-standaard schrijft geen daadwerkelijke code voor; het beschrijft functie-prototypes en vereist gedrag.

Met andere woorden: vanuit een strikt taalkundig oogpunt, bestaat de code die u ziet niet. Het kan afkomstig zijn uit een headerbestand dat bij de standaardbibliotheekimplementatie van uw compiler wordt geleverd. Merk op dat de C++-standaard niet eens vereist dat die header bestandenbestaan. Bestanden zijn slechts een handige manier voor implementators van compilers om te voldoen aan de vereisten voor een regel als #include <algorithm>(dwz std::removeen andere functies beschikbaar maken).

Waarom wordt ongelijkheid getest als if (!(*first == val))in plaats van if (*first != val)?

Omdat alleen operator==vereist is voor de functie.

Als het gaat om overbelasting van operators voor aangepaste typen, kun je met de taal allerlei rare dingen doen. Je zou heel goed een klasse kunnen maken met een overbelaste operator==maar geen overbelaste operator!=. Of nog erger: je zou operator!=kunnen overbelasten, maar het volledig ongerelateerde dingen laten doen.

Beschouw dit voorbeeld:

#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());
  // ...
}

Als std::removeoperator!=zou gebruiken, dan zou het resultaat heel anders zijn.


Antwoord 4, autoriteit 10%

Enkele goede antwoorden hier. Ik wilde alleen een kleine opmerking toevoegen.

Zoals alle goede bibliotheken, is de standaardbibliotheek ontworpen met (minstens) twee zeer belangrijke principes in gedachten:

  1. Leg gebruikers van uw bibliotheek zo min mogelijk verantwoordelijkheid waarmee u weg kunt komen. Een deel hiervan heeft te maken met het feit dat ze zo min mogelijk werk hoeven te doen bij het gebruik van uw interface. (zoals het definiëren van zo min mogelijk operators). Het andere deel heeft te maken met hen niet te verrassen of ze te verplichten om foutcodes te controleren (dus houd interfaces consistent en gooi uitzonderingen van <stdexcept>wanneer er iets misgaat).

  2. Elimineer alle logische redundantie. Alle vergelijkingen kunnen alleen worden afgeleid uit operator<, dus waarom eisen dat gebruikers anderen definiëren? bijv.:

    (a > b) is gelijk aan (b < a)

    (a >= b) is gelijk aan !(a < b)

    (a == b) is gelijk aan !((a < b) || (b < a))

    en ga zo maar door.

    Natuurlijk kan men zich bij deze opmerking afvragen waarom unordered_mapoperator==vereist (althans standaard) in plaats van operator<. Het antwoord is dat in een hashtabel de enige vergelijking die we ooit nodig hebben, een vergelijking is voor gelijkheid. Het is dus logisch consistenter(d.w.z. logischer voor de bibliotheekgebruiker) om te eisen dat ze een gelijkheidsoperator definiëren. Het vereisen van een operator<zou verwarrend zijn omdat het niet meteen duidelijk is waarom je het nodig hebt.


Antwoord 5, autoriteit 6%

Het EqualityComparableconcept alleenvereist dat operator==gedefinieerd wordt.

Daarom kan elke functie die beweert te werken met typen die voldoen aan EqualityComparablenietvertrouwen op het bestaan ​​van operator!=voor objecten van dat type . (tenzij er aanvullende vereisten zijn die het bestaan ​​van operator!=impliceren).


Antwoord 6

De meest veelbelovende aanpak is om een ​​methode te vinden om te bepalen of
operator== kan worden aangeroepen voor een bepaald type en het vervolgens ondersteunen
alleen wanneer deze beschikbaar is; in andere situaties zou een uitzondering zijn:
gegooid. Tot op heden is er echter geen manier bekend om te detecteren of een
willekeurige operator expressie f == g is op passende wijze gedefinieerd. Het beste
bekende oplossing heeft de volgende ongewenste eigenschappen:

  • Faalt tijdens het compileren voor objecten waar operator== niet toegankelijk is (bijvoorbeeld omdat het privé is).
  • Faalt tijdens het compileren als het aanroepen van operator== dubbelzinnig is.
  • Lijkt correct te zijn als de declaratie operator== correct is, ook al compileert operator== mogelijk niet.

Van Boost FAQ: bron

Wetend dat het een lastis om ==implementatie te vereisen, wil je nooit extra lasten creëren door ook !=implementatie te eisen.

Voor mij persoonlijk gaat het om SOLID(objectgeoriënteerd ontwerp) L part – Liskov substitutieprincipe: “objecten in een programma moeten vervangbaar zijn door instanties van hun subtypes zonder de correctheid van dat programma te veranderen. ”. In dit geval is het de operator !=die ik kan vervangen door ==en booleaanse inversein booleaanse logica.

Other episodes