What is the right way of initializing a static map? Do we need a static function that will initialize it?
Using C++11 initializer list {{},{},...}. The order of the initialized elements does not matter. The map will do the ordering by the key for you. If initializing unordered_map, it is the same principle where the sorted order will be determined by the hashing function and will appear to human eye as random:
#include <map>
using namespace std;
map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};
Using Boost.Assign:
#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;
map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');
Best way is to use a function:
#include <map>
using namespace std;
map<int,int> create_map()
{
map<int,int> m;
m[1] = 2;
m[3] = 4;
m[5] = 6;
return m;
}
map<int,int> m = create_map();
create_map
function running? It runs before main
, what is the name for that runtime? –
Innocency extern
variables will not have their correct values in this "before main run-time constructor" if the compiler only saw the extern
declaration, but has not run into the actual variable definition yet. –
Innocency const map<int,int> m = create_map()
(and so, initialize const members of a class in the initialization list: struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
–
Heliotype It's not a complicated issue to make something similar to boost. Here's a class with just three functions, including the constructor, to replicate what boost did (almost).
template <typename T, typename U>
class create_map
{
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
Usage:
std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);
The above code works best for initialization of global variables or static members of a class which needs to be initialized and you have no idea when it gets used first but you want to assure that the values are available in it.
If say, you've got to insert elements into an existing std::map... here's another class for you.
template <typename MapType>
class map_add_values {
private:
MapType mMap;
public:
typedef typename MapType::key_type KeyType;
typedef typename MapType::mapped_type MappedType;
map_add_values(const KeyType& key, const MappedType& val)
{
mMap[key] = val;
}
map_add_values& operator()(const KeyType& key, const MappedType& val) {
mMap[key] = val;
return *this;
}
void to (MapType& map) {
map.insert(mMap.begin(), mMap.end());
}
};
Usage:
typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);
See it in action with GCC 4.7.2 here: http://ideone.com/3uYJiH
############### EVERYTHING BELOW THIS IS OBSOLETE #################
EDIT: The map_add_values
class below, which was the original solution I had suggested, would fail when it comes to GCC 4.5+. Please look at the code above for how to add values to existing map.
template<typename T, typename U>
class map_add_values
{
private:
std::map<T,U>& m_map;
public:
map_add_values(std::map<T, U>& _map):m_map(_map){}
map_add_values& operator()(const T& _key, const U& _val)
{
m_map[key] = val;
return *this;
}
};
Usage:
std::map<int, int> my_map; // Later somewhere along the code map_add_values<int,int>(my_map)(1,2)(3,4)(5,6);
NOTE: Previously I used a operator []
for adding the actual values. This is not possible as commented by dalle.
##################### END OF OBSOLETE SECTION #####################
operator[]
only takes a single argument. –
Unseasonable error: conflicting declaration ‘map_add_values<int, int> my_map’
error: ‘my_map’ has a previous declaration as ‘std::map<int, int> my_map’
–
Jocasta map_add_values my_map...
. Are you using it like shown in my usage example? –
Joost class A{}; A a();
is fine. class A{}; A();
results in a compile error. –
Jocasta map_add_values<int,int>(my_map)(1,2)(3,4);
is like map_add_values<int, int> my_map(1,2)(3,4);
. I'm looking into how to come up with a an elegant solution. –
Joost class A {public: A(int){} void to(){}}; A(1).to();
–
Jocasta MAP_ADD(mymap,int,int, (1,2)(3,4));
–
Jocasta delete/free
if no new/malloc
used. 3. Arguments of template are types, not specific pointer, I don't get your point. –
Jocasta Here is another way that uses the 2-element data constructor. No functions are needed to initialize it. There is no 3rd party code (Boost), no static functions or objects, no tricks, just simple C++:
#include <map>
#include <string>
typedef std::map<std::string, int> MyMap;
const MyMap::value_type rawData[] = {
MyMap::value_type("hello", 42),
MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);
Since I wrote this answer C++11 is out. You can now directly initialize STL containers using the new initializer list feature:
const MyMap myMap = { {"hello", 42}, {"world", 88} };
For example:
const std::map<LogLevel, const char*> g_log_levels_dsc =
{
{ LogLevel::Disabled, "[---]" },
{ LogLevel::Info, "[inf]" },
{ LogLevel::Warning, "[wrn]" },
{ LogLevel::Error, "[err]" },
{ LogLevel::Debug, "[dbg]" }
};
If map is a data member of a class, you can initialize it directly in header by the following way (since C++17):
// Example
template<>
class StringConverter<CacheMode> final
{
public:
static auto convert(CacheMode mode) -> const std::string&
{
// validate...
return s_modes.at(mode);
}
private:
static inline const std::map<CacheMode, std::string> s_modes =
{
{ CacheMode::All, "All" },
{ CacheMode::Selective, "Selective" },
{ CacheMode::None, "None" }
// etc
};
};
std::array
–
Stavropol std::array
would be faster than a map
look up. –
Stavropol I would wrap the map inside a static object, and put the map initialisation code in the constructor of this object, this way you are sure the map is created before the initialisation code is executed.
Just wanted to share a pure C++ 98 work around:
#include <map>
std::map<std::string, std::string> aka;
struct akaInit
{
akaInit()
{
aka[ "George" ] = "John";
aka[ "Joe" ] = "Al";
aka[ "Phil" ] = "Sue";
aka[ "Smitty" ] = "Yando";
}
} AkaInit;
You can try:
std::map <int, int> mymap =
{
std::pair <int, int> (1, 1),
std::pair <int, int> (2, 2),
std::pair <int, int> (2, 2)
};
{1, 2}
instead of std::pair<int, int>(1, 2)
. –
Adamsite If you are stuck with C++98 and don't want to use boost, here there is the solution I use when I need to initialize a static map:
typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] =
{
elemPair_t( 1, 'a'),
elemPair_t( 3, 'b' ),
elemPair_t( 5, 'c' ),
elemPair_t( 7, 'd' )
};
const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );
This is similar to PierreBdR
, without copying the map.
#include <map>
using namespace std;
bool create_map(map<int,int> &m)
{
m[1] = 2;
m[3] = 4;
m[5] = 6;
return true;
}
static map<int,int> m;
static bool _dummy = create_map (m);
In addition to the good top answer of using
const std::map<int, int> m = {{1,1},{4,2},{9,3},{16,4},{32,9}}
there's an additional possibility by directly calling a lambda that can be useful in a few cases:
const std::map<int, int> m = []()->auto {
std::map<int, int> m;
m[1]=1;
m[4]=2;
m[9]=3;
m[16]=4;
m[32]=9;
return m;
}();
Clearly a simple initializer list is better when writing this from scratch with literal values, but it does open up additional possibilities:
const std::map<int, int> m = []()->auto {
std::map<int, int> m;
for(int i=1;i<5;++i) m[i*i]=i;
m[32]=9;
return m;
}();
(Obviously it should be a normal function if you want to re-use it; and this does require recent C++.)
You have some very good answers here, but I'm to me, it looks like a case of "when all you know is a hammer"...
The simplest answer of to why there is no standard way to initialise a static map, is there is no good reason to ever use a static map...
A map is a structure designed for fast lookup, of an unknown set of elements. If you know the elements before hand, simply use a C-array. Enter the values in a sorted manner, or run sort on them, if you can't do this. You can then get log(n) performance by using the stl::functions to loop-up entries, lower_bound/upper_bound. When I have tested this previously they normally perform at least 4 times faster than a map.
The advantages are many fold... - faster performance (*4, I've measured on many CPU's types, it's always around 4) - simpler debugging. It's just easier to see what's going on with a linear layout. - Trivial implementations of copy operations, should that become necessary. - It allocates no memory at run time, so will never throw an exception. - It's a standard interface, and so is very easy to share across, DLL's, or languages, etc.
I could go on, but if you want more, why not look at Stroustrup's many blogs on the subject.
map
is also a useful form for representing a partial function (function in the mathematical sense; but also, kind of, in the programming sense). An array does not do that. You can't, say, lookup data from an array using a string. –
Courtesy © 2022 - 2024 — McMap. All rights reserved.