Dit is de code uit de C++ standaardbibliotheek remove
code. 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::set
gebruikt operator<
(meer precies std::less
die 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 remove
code. 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::remove
en andere functies beschikbaar maken).
Waarom wordt ongelijkheid getest als
if (!(*first == val))
in plaats vanif (*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::remove
operator!=
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:
-
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). -
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_map
operator==
vereist (althans standaard) in plaats vanoperator<
. 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 eenoperator<
zou verwarrend zijn omdat het niet meteen duidelijk is waarom je het nodig hebt.
Antwoord 5, autoriteit 6%
Het EqualityComparable
concept alleenvereist dat operator==
gedefinieerd wordt.
Daarom kan elke functie die beweert te werken met typen die voldoen aan EqualityComparable
nietvertrouwen 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.