How to initialize a private static const map in C++?

I need just dictionary or associative array string => int.

There is type map C++ for this case.

But I need only one map forall instances(-> static) and this map can't be changed(-> const);

I have found this way with boost library

 std::map<int, char> example =
boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');

Is there other solution without this lib? I have tried something like this, but there are always some issues with map initialization.

class myClass{
private:
static map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
static map<int,int> myMap =  create_map();


};
174323 次浏览
#include <map>
using namespace std;


struct A{
static map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
static const map<int,int> myMap;


};


const map<int,int> A:: myMap =  A::create_map();


int main() {
}

A function call cannot appear in a constant expression.

try this: (just an example)

#include <map>
#include <iostream>


using std::map;
using std::cout;


class myClass{
public:
static map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
const static map<int,int> myMap;


};
const map<int,int>myClass::myMap =  create_map();


int main(){


map<int,int> t=myClass::create_map();
std::cout<<t[1]; //prints 2
}

If you find boost::assign::map_list_of useful, but can't use it for some reason, you could write your own:

template<class K, class V>
struct map_list_of_type {
typedef std::map<K, V> Map;
Map data;
map_list_of_type(K k, V v) { data[k] = v; }
map_list_of_type& operator()(K k, V v) { data[k] = v; return *this; }
operator Map const&() const { return data; }
};
template<class K, class V>
map_list_of_type<K, V> my_map_list_of(K k, V v) {
return map_list_of_type<K, V>(k, v);
}


int main() {
std::map<int, char> example =
my_map_list_of(1, 'a') (2, 'b') (3, 'c');
cout << example << '\n';
}

It's useful to know how such things work, especially when they're so short, but in this case I'd use a function:

a.hpp

struct A {
static map<int, int> const m;
};

a.cpp

namespace {
map<int,int> create_map() {
map<int, int> m;
m[1] = 2; // etc.
return m;
}
}


map<int, int> const A::m = create_map();

A different approach to the problem:

struct A {
static const map<int, string> * singleton_map() {
static map<int, string>* m = NULL;
if (!m) {
m = new map<int, string>;
m[42] = "42"
// ... other initializations
}
return m;
}


// rest of the class
}

This is more efficient, as there is no one-type copy from stack to heap (including constructor, destructors on all elements). Whether this matters or not depends on your use case. Does not matter with strings! (but you may or may not find this version "cleaner")

If the map is to contain only entries that are known at compile time and the keys to the map are integers, then you do not need to use a map at all.

char get_value(int key)
{
switch (key)
{
case 1:
return 'a';
case 2:
return 'b';
case 3:
return 'c';
default:
// Do whatever is appropriate when the key is not valid
}
}

I often use this pattern and recommend you to use it as well:

class MyMap : public std::map<int, int>
{
public:
MyMap()
{
//either
insert(make_pair(1, 2));
insert(make_pair(3, 4));
insert(make_pair(5, 6));
//or
(*this)[1] = 2;
(*this)[3] = 4;
(*this)[5] = 6;
}
} const static my_map;

Sure it is not very readable, but without other libs it is best we can do. Also there won't be any redundant operations like copying from one map to another like in your attempt.

This is even more useful inside of functions: Instead of:

void foo()
{
static bool initComplete = false;
static Map map;
if (!initComplete)
{
initComplete = true;
map= ...;
}
}

Use the following:

void bar()
{
struct MyMap : Map
{
MyMap()
{
...
}
} static mymap;
}

Not only you don't need here to deal with boolean variable anymore, you won't have hidden global variable that is checked if initializer of static variable inside function was already called.

The C++11 standard introduced uniform initialization which makes this much simpler if your compiler supports it:

//myClass.hpp
class myClass {
private:
static map<int,int> myMap;
};


//myClass.cpp
map<int,int> myClass::myMap = {
{1, 2},
{3, 4},
{5, 6}
};

See also this section from Professional C++, on unordered_maps.

If you are using a compiler which still doesn't support universal initialization or you have reservation in using Boost, another possible alternative would be as follows

std::map<int, int> m = [] () {
std::pair<int,int> _m[] = {
std::make_pair(1 , sizeof(2)),
std::make_pair(3 , sizeof(4)),
std::make_pair(5 , sizeof(6))};
std::map<int, int> m;
for (auto data: _m)
{
m[data.first] = data.second;
}
return m;
}();

Works fine without C++11

class MyClass {
typedef std::map<std::string, int> MyMap;
    

struct T {
const char* Name;
int Num;
    

operator MyMap::value_type() const {
return std::pair<std::string, int>(Name, Num);
}
};


static const T MapPairs[];
static const MyMap TheMap;
};


const MyClass::T MyClass::MapPairs[] = {
{ "Jan", 1 }, { "Feb", 2 }, { "Mar", 3 }
};


const MyClass::MyMap MyClass::TheMap(MapPairs, MapPairs + 3);

You could try this:

MyClass.h

class MyClass {
private:
static const std::map<key, value> m_myMap;
static const std::map<key, value> createMyStaticConstantMap();
public:
static std::map<key, value> getMyConstantStaticMap( return m_myMap );
}; //MyClass

MyClass.cpp

#include "MyClass.h"


const std::map<key, value> MyClass::m_myMap = MyClass::createMyStaticConstantMap();


const std::map<key, value> MyClass::createMyStaticConstantMap() {
std::map<key, value> mMap;
mMap.insert( std::make_pair( key1, value1 ) );
mMap.insert( std::make_pair( key2, value2 ) );
// ....
mMap.insert( std::make_pair( lastKey, lastValue ) );
return mMap;
} // createMyStaticConstantMap

With this implementation your classes constant static map is a private member and can be accessible to other classes using a public get method. Otherwise since it is constant and can not change, you can remove the public get method and move the map variable into the classes public section. I would however leave the createMap method private or protected if inheritance and or polymorphism is required. Here are some samples of use.

 std::map<key,value> m1 = MyClass::getMyMap();
// then do work on m1 or
unsigned index = some predetermined value
MyClass::getMyMap().at( index ); // As long as index is valid this will
// retun map.second or map->second value so if in this case key is an
// unsigned and value is a std::string then you could do
std::cout << std::string( MyClass::getMyMap().at( some index that exists in map ) );
// and it will print out to the console the string locted in the map at this index.
//You can do this before any class object is instantiated or declared.


//If you are using a pointer to your class such as:
std::shared_ptr<MyClass> || std::unique_ptr<MyClass>
// Then it would look like this:
pMyClass->getMyMap().at( index ); // And Will do the same as above
// Even if you have not yet called the std pointer's reset method on
// this class object.


// This will only work on static methods only, and all data in static methods must be available first.

I had edited my original post, there was nothing wrong with the original code in which I posted for it compiled, built and ran correctly, it was just that my first version I presented as an answer the map was declared as public and the map was const but wasn't static.

You can use the singleton pattern for this.

// The static pointer is initialized exactly once which ensures that
// there is exactly one copy of the map in the program, it will be
// initialized prior to the first access, and it will not be destroyed
// while the program is running.
class myClass {
private:
static std::map<int,int> myMap() {
static const auto* myMap = new std::map<int,int> {
{1, 2},
{3, 4},
{5, 6}
};
return *myMap;
}
}

You can then use your map like this

int x = myMap()[i] //where i is a key in the map