Ich bin ein C ++ - Programmierer mit begrenzter Erfahrung.
Angenommen, ich möchte ein STL map
zum Speichern und Bearbeiten einiger Daten verwenden, dann würde ich gerne wissen, ob zwischen diesen beiden Datenstrukturansätzen ein bedeutender Unterschied (auch in Bezug auf die Leistung) besteht:
Choice 1:
map<int, pair<string, bool> >
Choice 2:
struct Ente {
string name;
bool flag;
}
map<int, Ente>
Gibt es einen Overhead, der eine struct
anstelle einer einfachen verwendet pair
?
c++
data-structures
stl
Marco Stramezzi
quelle
quelle
std::pair
ist eine Struktur.std::pair
ist eine Vorlage .std::pair<string, bool>
ist eine Struktur.pair
ist völlig ohne Semantik. Niemand, der Ihren Code liest (auch Sie in Zukunft), wird wissen, dass diese.first
der Name von etwas ist, es sei denn, Sie weisen ausdrücklich darauf hin. Ich bin fest davon überzeugt, dass diespair
eine sehr schlechte und faule Ergänzung warstd
, und dass, als es konzipiert wurde, niemand dachte, "aber eines Tages wird jeder dies für alles verwenden , was zwei Dinge sind, und niemand wird wissen, was jemandes Code bedeutet ".map
Iteratoren keine gültigen Ausnahmen sind. ("erste" = Schlüssel und "zweite" = Wert ... wirklichstd
? Wirklich?)Antworten:
Wahl 1 ist in Ordnung für kleine "nur einmal verwendete" Dinge. Im Wesentlichen
std::pair
ist immer noch eine Struktur. Wie aus diesem Kommentar hervorgeht, führt Auswahl 1 zu wirklich hässlichem Code irgendwo im Kaninchenbau,thing.second->first.second->second
und niemand möchte das wirklich entschlüsseln.Wahl 2 ist besser für alles andere, weil es einfacher ist, die Bedeutung der Dinge auf der Karte zu erkennen. Es ist auch flexibler, wenn Sie die Daten ändern möchten (zum Beispiel, wenn Ente plötzlich ein anderes Flag benötigt). Leistung sollte hier kein Thema sein.
quelle
Leistung :
Es hängt davon ab, ob.
In Ihrem speziellen Fall wird es keinen Leistungsunterschied geben, da die beiden in ähnlicher Weise im Speicher angeordnet werden.
In einem ganz bestimmten Fall (wenn Sie eine leere Struktur als eines der Datenelemente verwenden) kann die
std::pair<>
EBO-Funktion (Empty Base Optimization) möglicherweise verwendet werden und eine geringere Größe als die entsprechende Struktur aufweisen. Und eine geringere Größe bedeutet im Allgemeinen eine höhere Leistung:Drucke: 32, 32, 40, 40 auf Ideone .
Hinweis: Mir ist keine Implementierung bekannt, die den EBO-Trick für reguläre Paare verwendet, er wird jedoch im Allgemeinen für Tupel verwendet.
Lesbarkeit :
Abgesehen von Mikrooptimierungen ist eine benannte Struktur jedoch ergonomischer.
Ich meine,
map[k].first
ist zwar nicht so schlimm,get<0>(map[k])
ist aber kaum verständlich. Ein Kontrast,map[k].name
der sofort anzeigt, woraus wir lesen.Umso wichtiger ist es, wenn die Typen untereinander konvertierbar sind, da ein versehentliches Vertauschen zu einem echten Problem wird.
Vielleicht möchten Sie auch mehr über strukturelle oder nominelle Typisierung lesen.
Ente
ist ein spezifischer Typ, der nur von Dingen bearbeitetEnte
werden kann, die es erwarten , und alles, was es bearbeitet,std::pair<std::string, bool>
kann es bearbeiten ... selbst wenn dasstd::string
oderbool
nicht das enthält, was es erwartet, weilstd::pair
keine Semantik damit verbunden ist.Wartung :
In Bezug auf die Wartung
pair
ist das schlimmste. Sie können kein Feld hinzufügen.tuple
Diesbezüglich ist es besser, wenn Sie das neue Feld anhängen und auf alle vorhandenen Felder mit demselben Index zugegriffen wird. Das ist so unergründlich wie zuvor, aber zumindest müssen Sie sie nicht aktualisieren.struct
ist der klare Gewinner. Sie können Felder hinzufügen, wo immer Sie möchten.Abschließend:
pair
ist das Schlimmste von beiden Welten,tuple
kann in einem ganz bestimmten Fall eine leichte Kante haben (leerer Typ),struct
.Hinweis: Wenn Sie Getter verwenden, können Sie den Trick mit der leeren Basis selbst anwenden, ohne dass die Clients darüber Bescheid wissen müssen
struct Thing: Empty { std::string name; }
. Aus diesem Grund ist Encapsulation das nächste Thema, mit dem Sie sich befassen sollten.quelle
first
undsecond
es gibt keinen Platz für die Optimierung der leeren Basis, um einzutreten.pair
, das EBO verwenden würde, aber ein Compiler könnte ein eingebautes bereitstellen. Da diese jedoch Mitglieder sein sollen, kann es beobachtbar sein (zum Beispiel ihre Adressen überprüfen), in welchem Fall es nicht konform wäre.[[no_unique_address]]
, wodurch das Äquivalent von EBO für Mitglieder aktiviert wird.Pair scheint am besten, wenn es als Rückgabetyp einer Funktion zusammen mit einer destrukturierten Zuweisung unter Verwendung von std :: tie und der strukturierten Bindung von C ++ 17 verwendet wird. Mit std :: tie:
Verwenden der strukturierten Bindung von C ++ 17:
Ein schlechtes Beispiel für die Verwendung eines std :: pair (oder Tupels) wäre etwa so:
weil nicht klar ist , wenn std :: get <2> (player_data) aufrufen , was an der Position Index gespeichert 2. Lesbarkeit Denken Sie daran , und es offensichtlich für den Leser zu machen , was der Code tut wichtig . Beachten Sie, dass dies viel besser lesbar ist:
Im Allgemeinen sollten Sie über std :: pair und std :: tuple nachdenken, um mehr als ein Objekt von einer Funktion zurückzugeben. Die Faustregel, die ich verwende (und die auch von vielen anderen verwendet wurde), lautet, dass Objekte, die in einem std :: tuple oder std :: pair zurückgegeben werden, nur im Kontext eines Aufrufs einer Funktion "verwandt" sind, die sie zurückgibt oder im Kontext einer Datenstruktur, die sie miteinander verbindet (z. B. verwendet std :: map std :: pair als Speichertyp). Wenn die Beziehung an anderer Stelle in Ihrem Code vorhanden ist, sollten Sie eine Struktur verwenden.
Verwandte Abschnitte der Kernrichtlinien:
quelle