
pm100
Ich habe eine Karte, die ein DB-Objekt darstellt. Ich möchte “bekannte” Werte daraus bekommen
std::map<std::string, std::string> dbo;
...
std::string val = map["foo"];
alles in Ordnung, aber mir fällt auf, dass “foo” bei jedem Aufruf in eine temporäre Zeichenfolge umgewandelt wird. Sicherlich wäre es besser, einen konstanten std::string zu haben (natürlich ist es wahrscheinlich ein kleiner Overhead im Vergleich zu der Festplatten-IO, die gerade das Objekt abgerufen hat, aber ich denke, es ist immer noch eine gültige Frage). Was ist also die korrekte Redewendung für std::string-Konstanten?
zum Beispiel – ich kann haben
const std::string FOO = "foo";
in einem hdr, aber dann bekomme ich mehrere kopien
BEARBEITEN: Noch keine Antwort hat gesagt, wie std::string-Konstanten deklariert werden. Ignorieren Sie das ganze Problem mit Karte, STL usw. Viel Code ist stark std::string-orientiert (meiner ist es sicherlich) und es ist natürlich, Konstanten für sie zu wollen, ohne immer wieder für die Speicherzuweisung zu bezahlen
EDIT2: Zweitfrage, die per PDF von Manuel beantwortet wurde, herausgenommen, Beispiel für schlechte Redewendung hinzugefügt
EDIT3: Zusammenfassung der Antworten. Beachten Sie, dass ich diejenigen nicht aufgenommen habe, die die Erstellung einer neuen Zeichenfolgenklasse vorgeschlagen haben. Ich bin enttäuscht, weil ich gehofft hatte, dass es eine einfache Sache gibt, die nur in der Header-Datei funktioniert (wie const char * const ). Wie auch immer
a) von Markus b
std::map<int, std::string> dict;
const int FOO_IDX = 1;
....
dict[FOO_IDX] = "foo";
....
std:string &val = dbo[dict[FOO_IDX]];
b) von vlad
// str.h
extern const std::string FOO;
// str.cpp
const std::string FOO = "foo";
c) von Roger P
// really you cant do it
(b) scheint dem, was ich wollte, am nächsten zu kommen, hat aber einen fatalen Fehler. Ich kann keinen statischen Code auf Modulebene haben, der diese Zeichenfolgen verwendet, da sie möglicherweise noch nicht erstellt wurden. Ich habe über (a) nachgedacht und tatsächlich einen ähnlichen Trick beim Serialisieren des Objekts verwendet, den Index anstelle der Zeichenfolge gesendet, aber es schien eine Menge Klempnerarbeit für eine Allzwecklösung zu sein. So leider (c) gewinnt, gibt es nicht einfach const Idiom für std:string
Das Kopieren und Fehlen von “String-Literal-Optimierung” ist genau die Art und Weise, wie std::strings funktionieren, und Sie können nicht genau das bekommen, wonach Sie fragen. Das liegt teilweise daran, dass explizit auf virtuelle Methoden und dtor verzichtet wurde. Die Schnittstelle std::string ist eine Menge kompliziert ohne die sowieso.
Der Standard erfordert eine bestimmte Schnittstelle sowohl für std::string als auch für std::map, und diese Schnittstellen verbieten zufällig die gewünschte Optimierung (als “unbeabsichtigte Folge” ihrer anderen Anforderungen und nicht explizit). Zumindest verbieten sie es, wenn Sie tatsächlich alle groben Details des Standards befolgen möchten. Und das wollen Sie wirklich, besonders wenn es so einfach ist, eine andere String-Klasse für diese spezielle Optimierung zu verwenden.
Diese separate Zeichenfolgenklasse kann diese “Probleme” jedoch lösen (wie Sie sagten, ist dies selten ein Problem), aber leider hat die Welt dies getan number_of_programmers + 1
davon schon. Sogar angesichts dieser Neuerfindung des Rads fand ich es nützlich, eine StaticString-Klasse zu haben, die eine Teilmenge der Schnittstelle von std::string hat: mit begin/end, substr, find usw. Sie verbietet auch Änderungen (und passt zu String-Literalen auf diese Weise), speichert nur einen Zeichenzeiger und eine Größe. Sie müssen etwas aufpassen, dass es nur mit Zeichenfolgenliteralen oder anderen “statischen” Daten initialisiert wird, aber das wird durch die Konstruktionsschnittstelle etwas gemildert:
struct StaticString {
template<int N>
explicit StaticString(char (&data)[N]); // reference to char array
StaticString(StaticString const&); // copy ctor (which is very cheap)
static StaticString from_c_str(char const* c_str); // static factory function
// this only requires that c_str not change and outlive any uses of the
// resulting object(s), and since it must also be called explicitly, those
// requirements aren't hard to enforce; this is provided because it's explicit
// that strlen is used, and it is not embedded-'\0'-safe as the
// StaticString(char (&data)[N]) ctor is
operator char const*() const; // implicit conversion "operator"
// here the conversion is appropriate, even though I normally dislike these
private:
StaticString(); // not defined
};
Verwenden:
StaticString s ("abc");
assert(s != "123"); // overload operators for char*
some_func(s); // implicit conversion
some_func(StaticString("abc")); // temporary object initialized from literal
Beachten Sie, dass der Hauptvorteil dieser Klasse ausdrücklich darin besteht, das Kopieren von Zeichenfolgendaten zu vermeiden, sodass der Zeichenfolgenliteralspeicher wiederverwendet werden kann. Es gibt einen besonderen Platz in der ausführbaren Datei für diese Daten, und sie ist im Allgemeinen gut optimiert, da sie aus den frühesten Tagen von C und darüber hinaus stammt. Tatsächlich glaube ich, dass diese Klasse dem nahe kommt, was String-Literale in C++ hätten sein sollen, wenn da nicht die C-Kompatibilitätsanforderung wäre.
Als Erweiterung könnten Sie auch Ihre eigene Kartenklasse schreiben, wenn dies ein wirklich häufiges Szenario für Sie ist, und das könnte einfacher sein, als Zeichenfolgentypen zu ändern.
Ganz einfach: verwenden
extern const std::string FOO;
in Ihrer Kopfzeile und
const std::string FOO("foo");
im angemessenen .cpp
Datei.

Manuel
-
Es ist möglich, den Aufwand für die Erstellung einer std::string
wenn alles, was Sie wollen, eine konstante Zeichenfolge ist. Dafür müssen Sie jedoch eine spezielle Klasse schreiben, da es in der STL oder in Boost nichts Vergleichbares gibt. Oder eine bessere Alternative ist die Verwendung einer Klasse wie StringPiece
aus Chrom bzw StringRef
von LLVM. Weitere Informationen finden Sie in diesem verwandten Thread.
-
Wenn Sie sich entscheiden, bei zu bleiben std::string
(was Sie wahrscheinlich tun werden), dann ist eine weitere gute Option die Verwendung des Boost MultiIndex-Containers, der die folgende Funktion hat (quoting die Dokumente):
Erhöhen Sie den MultiIndex […] stellt Nachschlageoperationen bereit, die Suchschlüssel akzeptieren, die sich vom key_type des Index unterscheiden, was eine besonders nützliche Einrichtung ist, wenn key_type-Objekte teuer zu erstellen sind.
Karten mit teuren Schlüsseln von Andrei Alexandrescu (C/C++-BenutzerjournalFeb. 2006) bezieht sich auf Ihr Problem und ist sehr gut zu lesen.
Die richtige Redewendung ist die, die Sie verwenden. In 99,99 % der Fälle müssen Sie sich keine Gedanken über den Overhead des Konstruktors von std::string machen.
Ich frage mich, ob der Konstruktor von std::string von einem Compiler in eine intrinsische Funktion umgewandelt werden könnte? Theoretisch wäre es möglich, aber mein obiger Kommentar wäre Erklärung genug dafür, warum es nicht passiert ist.
Es scheint, dass Sie bereits wissen, was die Zeichenfolgenliterale zur Laufzeit sein werden, sodass Sie eine interne Zuordnung zwischen Aufzählungswerten und einem Array von Zeichenfolgen einrichten können. Dann würden Sie die Enumeration anstelle eines tatsächlichen const char*-Literals in Ihrem Code verwenden.
enum ConstStrings
{
MAP_STRING,
FOO_STRING,
NUM_CONST_STRINGS
};
std::string constStrings[NUM_CONST_STRINGS];
bool InitConstStrings()
{
constStrings[MAP_STRING] = "map";
constStrings[FOO_STRING] = "foo";
}
// Be careful if you need to use these strings prior to main being called.
bool doInit = InitConstStrings();
const std::string& getString(ConstStrings whichString)
{
// Feel free to do range checking if you think people will lie to you about the parameter type.
return constStrings[whichString];
}
Dann würden Sie sagen map[getString(MAP_STRING)]
oder ähnliches.
Erwägen Sie außerdem, den Rückgabewert als konstanten Verweis zu speichern, anstatt ihn zu kopieren, wenn Sie ihn nicht ändern müssen:
const std::string& val = map["foo"];

jupp0r
In C ++ 14 können Sie dies tun
const std::string FOO = "foo"s;

Thomas Matthäus
Das Problem ist das std::map
kopiert den Schlüssel und die Werte in seine eigenen Strukturen.
Du könntest eine haben std::map<const char *, const char *>
, aber Sie müssten funktionale Objekte (oder Funktionen) bereitstellen, um die Schlüssel- und Wertdaten zu vergleichen, da diese Schablone für Zeiger gedacht ist. Standardmäßig ist die map
würde Zeiger vergleichen und nicht die Daten, auf die die Zeiger zeigen.
Der Kompromiss ist eine einmalige Kopie (std::string
) gegenüber dem Zugriff auf einen Komparator (const char *
).
Eine andere Alternative ist, eigene zu schreiben map
Funktion.
10155700cookie-checkKorrektes Idiom für std::string-Konstanten?yes
In Bezug auf die Bearbeitung habe ich versucht, das zu beheben (“nein, so funktioniert std::string nicht” und “hier ist eine andere Zeichenfolgenklasse”), aber beachten Sie, dass, wenn Sie einfach const std::strings irgendwo als global speichern möchten, bei einer ref-counted implementierung (die gcc bereitstellt und der standard explizit zulässt) müssten sie die allokation nur einmal (beim programmstart) bezahlen. Stellen Sie nur sicher, dass diese Globals sind konst um alle möglichen logischen Fehler leicht zu vermeiden.
– Roger Pate
22. Februar 2010 um 18:41 Uhr
Ihre Antwort war also – es ist nicht möglich. Es war nicht klar. Wenn niemand eine bessere Antwort findet, erhalten Sie das Häkchen
– pm100
22. Februar 2010 um 18:43 Uhr
Eigentlich gefällt mir Manuels Antwort derzeit. Entschuldigung, dass ich mich nicht klar genug ausgedrückt habe, deshalb habe ich den Kommentar abgegeben, aber ich habe mich auch versehentlich in eine andere Option eingeschlichen. 🙂
– Roger Pate
22. Februar 2010 um 18:54 Uhr
aber er sagt immer noch nicht, wie man std::string-Konstanten macht.
– pm100
22. Februar 2010 um 20:13 Uhr