Ich benutze viel std::set<int>
und oft muss ich einfach überprüfen, ob ein solches Set eine Nummer enthält oder nicht.
Ich würde es natürlich finden zu schreiben:
if (myset.contains(number))
...
Aber wegen des Fehlens eines contains
Mitglieds muss ich das umständliche schreiben:
if (myset.find(number) != myset.end())
..
oder das nicht so offensichtlich:
if (myset.count(element) > 0)
..
Gibt es einen Grund für diese Entwurfsentscheidung?
count()
Ansatzes besteht darin, dass er mehr Arbeit leistet alscountains()
nötig.contains()
der zurückkehrt einebool
würde wertvolle Informationen über verlieren , wo das Element in der Sammlung .find()
Bewahrt diese Informationen auf und gibt sie in Form eines Iterators zurück. Daher ist dies eine bessere Wahl für eine generische Bibliothek wie STL. (Das soll nicht heißen, dass abool contains()
nicht sehr schön zu haben oder sogar notwendig ist.)contains(set, element)
kostenlose Funktion über die öffentliche Oberfläche des Sets zu schreiben . Daher ist die Schnittstelle des Sets funktional vollständig. Durch Hinzufügen einer Convenience-Methode wird lediglich die Benutzeroberfläche vergrößert, ohne dass zusätzliche Funktionen aktiviert werden. Dies ist nicht die C ++ - Methode.Antworten:
Ich denke , es war wahrscheinlich , weil sie versuchen , zu machen
std::set
undstd::multiset
so ähnlich wie möglich. (Und hat offensichtlichcount
eine durchaus vernünftige Bedeutung fürstd::multiset
.)Persönlich denke ich, dass dies ein Fehler war.
Es sieht nicht ganz so schlecht aus, wenn Sie so tun,
count
als wäre dies nur eine Rechtschreibfehler,contains
und den Test wie folgt schreiben:Es ist immer noch eine Schande.
quelle
.end()
).contains()
, da es überflüssig wäre, da für jedesstd::set<T> s
undT t
das Ergebnis vons.contains(t)
genau mit dem Ergebnis von identisch iststatic_cast<bool>(s.count(t))
. Da die Verwendung des Werts in einem bedingten Ausdruck ihn implizit umwandeln würdebool
, haben sie möglicherweise das Gefühl, dass diescount()
dem Zweck gut genug diente.if (myset.ICanHaz(element)) ...
: DUm schreiben zu können
if (s.contains())
,contains()
muss einbool
(oder ein konvertierbarer Typbool
, der eine andere Geschichte ist) zurückgegeben werden, wie es derbinary_search
Fall ist.Der wesentliche Grund hinter der Design - Entscheidung nicht es auf diese Art und Weise zu tun , ist , dass
contains()
der zurückgibt einerbool
würde wertvolle Informationen über verlieren , wo das Element in der Sammlung .find()
Bewahrt diese Informationen auf und gibt sie in Form eines Iterators zurück. Daher ist dies eine bessere Wahl für eine generische Bibliothek wie STL. Dies war schon immer das Leitprinzip für Alex Stepanov, wie er oft erklärt hat (zum Beispiel hier ).Was den
count()
Ansatz im Allgemeinen betrifft , so ist das Problem, obwohl er oft in Ordnung ist, dass er mehr Arbeit leistet, als eincontains()
zu tun hätte .Das heißt nicht, dass a
bool contains()
nicht sehr schön zu haben oder sogar notwendig ist. Vor einiger Zeit hatten wir in der Gruppe ISO C ++ Standard - Future Proposals eine lange Diskussion über dieses Problem.quelle
contains()
dies offensichtlich stark mit vorhandenen Containern und Algorithmen interagieren würde, was stark von Konzepten und Bereichen beeinflusst würde - zu der Zeit, die voraussichtlich in C ++ 17 verfügbar sein wird - und Ich war überzeugt (als Ergebnis der Diskussion sowie einiger privater E-Mail-Austausche), dass es eine bessere Idee war, zuerst auf sie zu warten. Natürlich war 2015 nicht klar, dass weder Konzepte noch Bereiche es nicht in C ++ 17 schaffen würden (tatsächlich gab es große Hoffnungen, dass sie es schaffen würden). Ich bin mir jedoch nicht sicher, ob es sich lohnt, es jetzt zu verfolgen.std::set
(worum es in der Frage geht), ich sehe nicht, wiecount
mehr funktioniert, alscontains
nötig wäre. Die glibc-Implementierung voncount
ist (ungefähr)return find(value) == end() ? 0 : 1;
. Abgesehen von Details über den ternären Operator und nicht nur über die Rückkehr!= end()
(von denen ich erwarten würde, dass sie vom Optimierer entfernt werden), kann ich nicht sehen, wie es noch mehr Arbeit gibt.myset.contains()
(falls vorhanden), wäre dies ein ziemlich starker Hinweis darauf, dass diese Informationen nicht wertvoll sind ( an den Benutzer in diesem Zusammenhang).count()
mehr Arbeit alscontains()
nötigstd::set
? Es ist einzigartig,count()
könnte alsoreturn contains(x) ? 1 : 0;
genau das gleiche sein.Es fehlt es, weil niemand es hinzugefügt hat. Niemand hat es hinzugefügt, weil die Container aus der STL, die in der
std
Bibliothek enthalten waren, so konzipiert wurden, dass sie nur eine minimale Schnittstelle haben. (Beachten Sie, dassstd::string
dies nicht auf die gleiche Weise von der STL kam).Wenn Ihnen eine seltsame Syntax nichts ausmacht, können Sie sie vortäuschen:
verwenden:
Grundsätzlich können Sie
std
mit dieser Technik Erweiterungsmethoden für die meisten C ++ - Typen schreiben .Es ist viel sinnvoller, dies einfach zu tun:
aber ich bin amüsiert von der Methode der Erweiterungsmethode.
Das wirklich Traurige ist, dass das Schreiben eines effizienten
contains
auf einemmultimap
oder schneller sein kannmultiset
, da sie nur ein Element finden müssen, währendcount
sie jedes von ihnen finden und zählen müssen .Ein Multiset mit 1 Milliarde Kopien von 7 (Sie wissen, falls Sie keine mehr haben) kann sehr langsam sein
.count(7)
, aber sehr schnellcontains(7)
.Mit der obigen Erweiterungsmethode könnten wir es für diesen Fall schneller machen
lower_bound
, indem wirend
das Element verwenden , mit ihm vergleichen und dann mit ihm vergleichen. Dies sowohl für einen ungeordneten als auch für einen geordneten Miau zu tun, würde jedoch ausgefallene SFINAE- oder container-spezifische Überladungen erfordern.quelle
std::set
kann keine Duplikate enthalten und werde daherstd::set::count
immer zurückkehren0
oder1
.std::multiset::count
canbackticks
um das Wort "set" ist, weil ich mich nichtstd::set
speziell beziehe . Damit Sie sich besser fühlen, füge ich MultiSie untersuchen einen bestimmten Fall und sehen kein größeres Bild. Wie in der Dokumentation angegeben,
std::set
erfüllt es die Anforderungen des AssociativeContainer- Konzepts. Für dieses Konzept macht es keinen Sinn, einecontains
Methode zu haben , da sie fürstd::multiset
und so gut wie nutzlos iststd::multimap
, abercount
für alle gut funktioniert. Obwohl Verfahrencontains
als Alias hinzugefügt werden könnten fürcount
fürstd::set
,std::map
und deren Hash - Versionen (wielength
fürsize()
instd::string
), aber sieht aus wie Bibliothek Schöpfer nicht wirkliche Notwendigkeit dafür sehen.quelle
string
ein Monster ist: Es existierte vor der STL, wo es hattelength
und all jene Methoden, die indexbasiert sind, und wurde dann "containerisiert", um in das STL-Modell zu passen ... ohne die vorhandenen Methoden aus Gründen der Abwärtskompatibilität zu entfernen . Siehe GotW # 84: Monoliths Unstrung =>string
verstößt wirklich gegen das Entwurfsprinzip "Mindestanzahl an Elementfunktionen ".contains
ist der Aufwand für einen Satz / eine Karte gleich, kann jedoch schneller alscount
für einen Multiset / eine Multimap erfolgen.contains
Methode.size()
undempty()
sind Duplikate, aber viele Container haben beides.Obwohl ich nicht weiß, warum
std::set
nein,contains
abercount
was immer nur zurückkehrt0
oder1
, können Sie eine Vorlagen-contains
Hilfsfunktion wie diese schreiben :Und benutze es so:
quelle
contains
Methode tatsächlich existiert, wird sie nur auf dumme Weise benannt.std::string
Hustenstd.::string
ist NICHT Teil der STL! Es ist Teil der Standardbibliothek und wurde rückwirkend als Vorlage verwendet ...count
kann langsamer sein, da effektiv zwei Sekunden benötigt werden,find
um den Anfang und das Ende des Bereichs zu erhalten, wenn der Code gemeinsam genutzt wirdmultiset
.contains
. Daran sehe ich nichts auszusetzen. @MarkRansom Die kleine SFINAE soll verhindern, dass diese Vorlage an Dinge gebunden wird, die sie nicht sollte.Der wahre Grund dafür
set
ist für mich ein Rätsel, aber eine mögliche Erklärung für dasselbe Designmap
könnte darin bestehen, zu verhindern, dass Leute versehentlich ineffizienten Code schreiben:Was zu zwei
map
Suchvorgängen führen würde.Stattdessen müssen Sie einen Iterator erhalten. Dies gibt Ihnen einen mentalen Hinweis, dass Sie den Iterator wiederverwenden sollten:
das verbraucht nur eine
map
Suche.Wenn wir das erkennen
set
undmap
aus demselben Fleisch bestehen, können wir dieses Prinzip auch auf anwendenset
. Das heißt, wenn wirset
nur dann auf ein Element einwirken möchten, wenn es in der vorhanden istset
, kann dieses Design uns daran hindern, Code wie folgt zu schreiben:Natürlich ist das alles nur eine Spekulation.
quelle
if (myMap.count("Meaning of universe"))
, also ...?contains
wie mit schreibencount
.Seit c ++ 20,
bool contains( const Key& key ) const
ist verfügbar.
quelle
Was ist mit binary_search?
quelle
std::unordered_set
, aber dafürstd::set
würde es funktionieren .enthält () muss einen Bool zurückgeben. Mit dem C ++ 20 Compiler erhalte ich folgende Ausgabe für den Code:
quelle
Ein weiterer Grund ist, dass es einem Programmierer den falschen Eindruck vermitteln würde, dass std :: set eine Menge im Sinne der mathematischen Mengenlehre ist. Wenn sie das implementieren, würden viele andere Fragen folgen: Wenn ein std :: set () für einen Wert enthält, warum hat es ihn nicht für einen anderen Satz? Wo sind Union (), Intersection () und andere Mengenoperationen und Prädikate?
Die Antwort ist natürlich, dass einige der Mengenoperationen bereits als Funktionen in (std :: set_union () usw.) implementiert sind und andere ebenso trivial implementiert sind wie enthält (). Funktionen und Funktionsobjekte funktionieren mit mathematischen Abstraktionen besser als Objektelemente und sind nicht auf den jeweiligen Containertyp beschränkt.
Wenn eine vollständige Mathe-Set-Funktionalität implementiert werden muss, hat er nicht nur die Wahl des zugrunde liegenden Containers, sondern auch die Wahl der Implementierungsdetails, z. B. würde seine Funktion Theory_union () mit unveränderlichen Objekten funktionieren, die besser für die funktionale Programmierung geeignet sind , oder würde es seine Operanden ändern und Speicher sparen? Wäre es von Anfang an als Funktionsobjekt implementiert oder wäre es besser, eine C-Funktion zu implementieren und bei Bedarf std :: function <> zu verwenden?
So wie es jetzt ist, ist std :: set nur ein Container, der sich gut für die Implementierung von set im mathematischen Sinne eignet, aber es ist fast so weit von einer theoretischen Menge entfernt wie std :: vector von einem theoretischen Vektor.
quelle