2013-05-23 11 views
6

Ich brauche eine std: map Datenstruktur, die nur gelesen wird, was bedeutet, ich muss es einmal mit Daten füllen und dann nur diese Werte lesen, nie ändern oder zusätzliche hinzufügen.Wie erstellt man eine std :: map von konstanten Werten, auf die der Operator [] noch zugreifen kann?

Meine Nicht-const-Version sieht wie folgt aus:

//in .h 
#include <string> 
#include <map> 

std::map<std::string, int> myMap; 
void initMap(); 

//in .cpp 
#include "foo.h" 

void initMap() { 
    myMap["Keys"] = 42; 
} 

Dann würde ich initMap() einmal in meinem Code aufrufen und durchgeführt werden.

Jetzt habe ich schon einige Fragen hier gelesen und es scheint nicht-trivial zu sein, um Const-ness für die Karte zu erreichen.

Making it a std::map<std::string, const int> wird es mir nicht erlauben, es in der initMap() zu füllen. Füllen Sie es mit einem nicht-Konst-Temp und der Kopierkonstruktor für die Definition funktioniert auch nicht, da der Copy-Konstruktor nicht leicht die nicht-const-Version als Eingabe nimmt.

Es const std::map<std::string, int> machen (die ich mit einer nicht-const-Kopie während der Definition füllen konnte) würde die Verwendung des []-Operators für den Wert-Zugriff deaktivieren.

Also gibt es eine Möglichkeit, (Wert) Const-Ness zu erreichen und die Struktur zu initialisieren (vorzugsweise in der Header-Datei)?

BTW: Weder C++ 0x noch C++ 11 noch boost:: ist eine Option.

+0

Möchten Sie "eine std: Kartendatenstruktur, die schreibgeschützt ist", oder möchten Sie eine Karte mit schreibgeschützten Elementen? – juanchopanza

+0

Ich will das ehemalige, aber ich kann mit dem späteren leben, wenn dies mich den '[]' Operator hält. –

Antwort

13

Könnten Sie nicht die insert() Methode für std::map verfügbar verwenden?

http://www.cplusplus.com/reference/map/map/insert/

Edit: (Lösung) myMap.insert(std::pair<std::string, const int>("Keys", 42));

Soweit ich es verstehe, ist der Grund, warum dies funktioniert, weil der Konstruktor für das Paar, pair (const first_type& a, const second_type& b), seine Mitglieder first initialisiert und second die Konstrukteure mit für first_type und second_type, wobei a und b als ihre jeweiligen Parameter.

Mit der Lösung, die Sie versuchen, mein Verständnis zu verwenden, ist, dass myMap["Keys"] = 42; das Mitglied für intsecond des map (vom Typ const int) unter Verwendung des Standard-Konstruktor initialisiert. Dann wird versucht, diesem Mitglied einen Wert zuzuweisen. Da dies außerhalb des Konstruktors der Klasse map erfolgt, macht dies die const Deklaration unmöglich.

Bei der Lösung, die insert() verwendet, werden die Member im Konstruktor pair initialisiert. So können sie const deklariert werden. Die gleiche Operation wird ausgeführt, wenn die pair in die map kopiert wird.

+0

Erweitern Sie Ihre Antwort, um mehr wörtlich zu sein und tatsächlich meine Frage zu beantworten, und ich akzeptiere, ist wie richtig. In der Tat, indem ich mein eigenes Codebeispiel oben verwende, wird 'myMap [" Keys "] = 42;' by 'myMap.insert (std :: make_pair (" Keys ", 42))' für eine 'std :: map '. Aber ich habe keine Ahnung, warum das funktioniert und das andere nicht. –

+0

Und ich habe gerade verifiziert, es gibt mir korrekt einen Compilerfehler beim Schreibzugriff ('myMap [" Keys "] = 23;'), während Lesezugriff erlaubt ist ('std :: cout << myMap [" Keys "]' ') . –

+0

@ Chaos_99 Entschuldigung. Am liebsten hätte ich es als Kommentar geschrieben, aber das darf ich noch nicht. – Matzar

7

Während dies für Sie nicht möglich ist, andere, die dies tun wollen und das ein C++ 11 kompatibel Compiler könnte uniform initialization verwenden:

std::map<std::string, const int> myMap = { 
    { "keys", 42 } 
}; 

Oh, und übrigens, don 't define die Karte in der Header-Datei. Stattdessen deklarieren Sie es als extern in der Header-Datei, dann definieren Sie es in der Quelldatei.

+0

Ich habe die ursprüngliche Frage bearbeitet. Kein C++ 0x bedeutet auch kein C++ 11. Aber Sie haben absolut Recht mit dem Deklarieren/Definieren. Vielen Dank! –

1

Wenn die map nicht mutieren kann, sollten Sie const map<string, int> verwenden und nicht map<string, const int>: Die zweite Version ermöglicht das Einfügen und Löschen von Objekten.

Leider müssen Sie den [] Operator verlieren; C++ hat kein ImmutableMap oder etwas ähnliches. Allerdings sind std::map::at und std::map::find nicht so schlecht ...

+0

Wenn es zwischen dem Operator [] und Einfügen/Löschen ist, würde ich mit Einfügen/Löschen gehen. Wenn man etwas nicht per Konvention verwendet, ist es leichter, es an andere Entwickler zu verkaufen, als an die klobige Zugriffssyntax. –

2

Die einfachste Lösung ist Ihre eigenen zu schreiben, die Standardkarte Klasse Verpackung:

template <typename KeyType, typename MappedType, typename CmpType> 
class ConstantMap 
{ 
    typedef std::map<KeyType, MappedType, CmpType> Impl; 
    Impl myImpl; 
public: 
    typedef Impl::value_type value_type; 

    template <ForwardIterator> 
    ConstantMap(ForwardIterator begin, ForwardIterator end, CmpType cmp = CmpType()) 
     : myImpl(begin, end, cmp) 
    { 
    } 

    // necessary if [] is not going to work for missing keys 
    bool contains(KeyType const& key) const 
    { 
     return myImpl.find(key) != myImpl.end(); 
    } 

    MappedType const& operator[](KeyType const& key) const 
    { 
     Impl::const_iterator elem = myImpl.find(key); 
     if (elem == myImpl.end()) { 
      // Not found, do what you want (maybe throw an exception) 
     } 
     return elem.second; 
    } 
}; 

Sie die Karte, indem es von etwas Iteratoren zu einer Sequenz initialze können, die zu value_type umwandeln können.

Je nach Bedarf, können Sie zusätzliche Forwarding typedefs hinzufügen möchten, Funktionen, etc. Wenn Sie C++ 11 verwenden, Sie können auch einen Konstruktor erstellen, die eine Liste initializer verwenden können.

+0

Danke für den Beispielcode der Wrapper-Klasse. Ich bin mir sicher, dass es vielen Leuten, die nach einer benutzerdefinierten Kartenklasse suchen, nützlich sein kann. Aber ich nehme die Lösung von @Matzar, solange ich nur auf Const-Ness angewiesen bin. –

0

Etwas viel einfacher, die eine kleinere Stellfläche und ist schneller:

static const int MyMapData[] = { 
    42  // index 0 is mapped to "key" 
};

struct MyMap { const int& operator[](std::string key) const { switch (key) { case "Keys": return MyMapData[0];

default: return NotANumber; // Return 0 and raise an assertion, or return "Not a Number". } }

};

Einfache Wartung, keine Verwendung von Vorlagen, keine Verwendung von Boost-Bibliotheken und überall kompilierbar.

+0

Könnte für dieses einfache Beispiel funktionieren, aber das Ausführen einer switch-Anweisung mit Tausenden von Optionen ist wahrscheinlich nicht schneller als ein std :: map-Zugriff. –

Verwandte Themen