Using a const key for unordered_map - c++

I've been switching my code over from std::map to std::unordered_map where appropriate. With std::map, I typically write the following just to make sure the key cannot be modified:
std::map<const std::string, int>
Frankly, I never checked if this const was of any value. This has always compiled and worked with g++.
Now, with std::unordered_map, the following fails to link with g++ 4.5.1.
std::unordered_map<const std::string, std::string> m;
m["foo"] = "bar";
with this link error:
Undefined symbols:
"std::hash<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const>::operator()(std::basic_string<char, std::char_traits<char>, std::allocator<char> >) const", referenced from:
The fix is simple, to remove const, but besides that, is there even a point in STL with any of the associative container classes to use a const key type? Are there no methods that let you get a reference to the key for any associative container?

The associative containers only expose the (key,value) pair as std::pair<const key_type, mapped_type>, so the additional const on the key type is superfluous.

std::unordered_map<std::string const, std::string> uses std::hash<std::string const>, but no specialization exists for hash<std::string const>. However, there is one for std::hash<std::string>. You can provide one if you wish to use a std::string const as your key:
struct constant_string_hash {
std::size_t operator () (std::string const &s) const {
return hash<std::string>{}(s);
}
};
You can then declare your std::unordered_map like this:
std::unordered_map<std::string const, std::string, constant_string_hash> m;
m["test key"] = "test value";

Related

Using accessors on reference to vector for iteration in C++

I am trying to iterate over a reference to a vector of strings inside a function. The code originally iterated over an internal copy of the vector but I would like to iterate over the original if possible. However, when I try to do so, I get type mismatch problems.
I tried to work with just portions of an iterator by setting and printing it, but I get similar type mismatching for both the initialization and printing with a %s format specifier. Inside gdb, printing the begin accessor works the same for the reference to the vector or a copy of that to its own vector.
Outside:
std::vector<std::string> foo;
foo.pushback('alpha');
foo.pushback('bravo');
func(foo);
Inside with copying:
void func(const std::vector<std::string> &bar){
std::vector<std::string> barcopy = bar;
for (std::vector<std::string>::iterator barIt = barcopy.begin(); barIt != barcopy.end(); barIt++){
//operations with the string inside barcopy
}
}
Inside without copying:
void func(const std::vector<std::string> &bar){
for (std::vector<std::string>::iterator barIt = bar.begin(); barIt != bar.end(); barIt++){
//operations with the string inside bar
}
}
I expected the reference to behave the same as the copy, but attempting this directly gets me the following when attempting to compile.
error: conversion from
'__gnu_cxx::__normal_iterator<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >'
to non-scalar type
'__gnu_cxx::__normal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >'
requested
What type does the begin accessor return when performed on a reference to a vector? How do I go about iterating over this reference without copying?
Because you are passing bar as a const ref, you need to change:
for (std::vector<std::string>::iterator barIt = bar.begin(); barIt != bar.end(); barIt++)
to:
for (std::vector<std::string>::const_iterator barIt = bar.cbegin(); barIt != bar.cend(); barIt++)
or, better still, use a ranged for loop, if all you want is to iterate through the elements in the vector:
for (auto &elem : bar)
Alternatively, change your function signature to void func(std::vector<std::string> &bar) (without the const). The ranged for loop will work in either case.
Notice that you are taking the parameter as a const reference. The error is due to trying to take a non-const iterator from a const container. You could fix the error by changing your code to: (take note of the iterator type)
for (std::vector<std::string>::const_iterator barIt = bar.begin(); barIt != bar.end(); barIt++){
//operations with the string inside bar
}
Alternatively, you could use the auto keyword which will deduce the correct iterator type:
for (auto barIt = bar.begin(); barIt != bar.end(); barIt++){
//operations with the string inside bar
}

map of map initialization

I am trying to initialize a map of map but I am unsure what mistake I am doing. Below is the sample code.
static std::map<std::string, std::map<std::string,std::string>> _ScalingMapVolume ={
{"AA",{"busy_timeout","test"}},
{"BB",{"cache_size","10000"}}
};
The error I am getting is;
error: no match for call to ‘(std::_Select1st<std::pair<const std::basic_string<char>, std::basic_string<char> > >) (const char&)’
{"busy_timeout","test"} is not the value of a map, but a pair. You need {{"busy_timeout","test"}}.
Your code should look like this:
static std::map<std::string, std::map<std::string, std::string>> _ScalingMapVolume = {
{"AA", {{"busy_timeout", "test"}}},
{"BB", {{"cache_size", "10000"}}}
};
init = {{"AA", {"busy_timeout", "test"}}, ...}
You are missing one set of braces, since the value_type of the map is std::pair<const std::string, std::map<std::string, std::string>>. The value_type of the mapped_type is
std::pair<const std::string, std::string>. So you need to use it that way.

c++, stl map of pointers to functions vectors

i seem to have some problem in creating a structure defined as the topic.
My objective is to create a sort of event handler, (it doesn't matter if it's good or bad programming,or if it is not multithread: for the moment is just for practice).
My idea is then to create a vector of pointers to functions, and place this vector in a map,where the key is a string.
i must be doing something conceptually wrong, because i get some strange errors:
My code is as following (errors are at the end):
.h file
//ptr to function
typedef int (*pt2Function)(void*);
typedef std::vector<pt2Function> fPtrVector;
class eventDispatcher
{
public:
//stuff
void addListener(std::string,pt2Function);
protected:
//stuff
std::map<std::string,fPtrVector> _listeners;
};
and here is the cpp:
.cpp file
void eventDispatcher::addListener(std::string eventName ,pt2Function function)
{
std::map<std::string,fPtrVector>::iterator it;
it=this->_listeners.find(eventName);
if(it != this->_listeners.end())
{
//do something
}
else
{
std::vector<pt2Function> tmp;
tmp.insert(function); // here occurs error 1
this->_listeners.insert(eventName,tmp); // here occurs error 2
std::cout<<"cnt: "<< this->_listeners.count();
}
}
The errors i get are:
1) no matching function for call to 'std::vector <int (*)(void*), std::allocator<int (*)(void*)> >::insert(int (*&)(void*))'
2) no matching function for call to 'std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<int (*)(void*), std::allocator<int (*)(void*)> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<int (*)(void*), std::allocator<int (*)(void*)> > > > >::insert(std::string&, std::vector<int (*)(void*), std::allocator<int (*)(void*)> >&)'
If you check a reference to insert you see that it takes two arguments, an iterator and the value. To just add a value use e.g push_back instead.
For the map, you can use _listeners[eventName] = tmp; instead.
No variation of std::vector::insert() takes a single argument: all take an iterator indicating the position to which the new element(s) must be inserted before. Use std::vector::push_back() to add function instead.
std::map::insert() has several variations, but the one you are attempting to use takes the value_type of the map, which is defined as:
value_type std::pair<const Key, T>
in your case the value_type is a std::pair<std::string, fPtrVector>:
this->_listeners.insert(std::pair(eventName,tmp));
1) The insert() method of std::vector works by specifying a position using an iterator. For your case, you can use push_back().
2) Use insert(std::make_pair(eventName, tmp)) and that will create the required value type by the map.
You are looking for std::vector::push_back (see the documentation for std::vector here).
You are looking for std::map::operator [] (see the documentation for std::map here).

assignment operator with maps

map<char *, int> sym_addr;
map<char *, int> sym_tbl;
void set_map(map<char *, int> & sym_tbl)
{
sym_addr = sym_tbl;
}
Is there any problem with the above assignment?
A better way would be to change the keys to std::string : std::map< std::string, int>
If you want to copy, pass that object by const reference:
typedef std::map< std::string, int> myMapType;
myMapType sym_addr;
myMapType sym_tbl;
void set_map(const myMapType & sym_tbl)
{
sym_addr = sym_tbl;
}
Other then that, there are no problems. map::operator= is used to copy the content of one map into another.
You should pass the argument by const reference, otherwise you prevent copying a map declared const.
Also, make sure you understand exactly what happens if you use char* as a key - the key is the address of the string, not its contents. If you want to index the map by string values, use std::string as the key.

C++ unordered_map user defined type

I am having a class which is used as the key in the unordered_map. When I tried to compiled the code, it shows undefined reference to std::hash<typeName>::operator()(typename) const. How could I go to fix it? What additional function do I need to overload to make the user defined type to be used in an unordered_map?
I have a dateTime struct which stores the info of date and time.
The error message is as follows:
In function 'std::__detail::_Hash_code_base<DateTime, std::pair<DateTime const, int>, std::_Select1st<std::pair<DateTime const, int> >, std::equal_to<DateTime>, std::hash<DateTime>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_hash_code(DateTime const&) const':
testing.cpp:(.text._ZNKSt8__detail15_Hash_code_baseI10DateTimeSt4pairIKS1_DeESt10_Select1stIS4_ESt8equal_toIS1_ESt4hashIS1_ENS_18_Mod_range_hashingENS_20_Default_ranged_hashELb0EE12_M_hash_codeERS3_[std::__detail::_Hash_code_base<DateTime, std::pair<DateTime const, int>, std::_Select1st<std::pair<DateTime const, int> >, std::equal_to<DateTime>, std::hash<DateTime>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_hash_code(DateTime const&) const]+0x23): undefined reference to 'std::hash<DateTime>::operator()(DateTime) const'
Thanks.
you have to implement hash algorithm, otherwise standard container will not pick your type, because it has no idea how to calculate hash code for it.
namespace std
{
template <>
struct hash<DateTime> : public unary_function<DateTime, size_t>
{
size_t operator()(const DateTime& v) const
{
return /* my hash algorithm */;
}
};
}