I am not too familiar with the STL's unordered map and I'm having trouble inserting elements into it.
So I have an unordered map where the key is a string and the mapped value is a vector of integers. I have it declared as:
unordered_map<string, vector<int> > categorySearch;
How would I go about inserting elements into this map. I am currently doing
categorySearch.insert(make_pair("hello", categorySearch["hello"].push_back(5)));
This is clearly giving me compiler errors. How do I use insert for the vector portion of the map.
Here is the compiler error I am getting:
logData.h: In member function ‘void LogData::addEntry(Log)’:
logData.h:23:68: error: no match for ‘operator[]’ (operand types are ‘std::unordered_map<std::basic_string<char>, std::vector<int> >’ and ‘<unresolved overloaded function type>’)
categorySearch.insert(make_pair(add.getCategory(), categorySearch[add.getCategory].push_back(data.size()-1)));
^
logData.h:23:68: note: candidates are:
In file included from /usr/um/gcc-4.8.2/include/c++/4.8.2/unordered_map:48:0,
from logData.h:2,
from logman.cpp:5:
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:595:7: note: std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = std::vector<int>; _Hash = std::hash<std::basic_string<char> >; _Pred = std::equal_to<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::vector<int> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::vector<int>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::basic_string<char>]
operator[](const key_type& __k)
^
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:595:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘const key_type& {aka const std::basic_string<char>&}’
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:599:7: note: std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = std::basic_string<char>; _Tp = std::vector<int>; _Hash = std::hash<std::basic_string<char> >; _Pred = std::equal_to<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::vector<int> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::vector<int>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::basic_string<char>]
operator[](key_type&& __k)
^
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:599:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘std::unordered_map<std::basic_string<char>, std::vector<int> >::key_type&& {aka std::basic_string<char>&&}’
make: *** [logman.o] Error 1
Thanks.
I think I'd take one of two approaches. The first possibility would be to use the map's operator[], so the code looked like:
category_search["hello"].push_back(5);
The other obvious possibility would be to recognize that what you're creating (each key having multiple mapped values) is equivalent to what an unordered_multimap provides, so you could just use:
std::unordered_multimap<std::string, int> category_search;
category_search.emplace("hello", 5);
Note that I've also used emplace to simplify the code (and as a bonus, probably run a little faster as well).
And yes, both of these support multiple values, so you could add:
category_search["hello"].push_back(10);
... or:
category_search.emplace("hello", 10);
...to the unordered_map/unordered_multimap respectively to also associate the value 10 with the key hello.
In the case of the unordered_multimap, you'd retrieve the set of values associated with a particular key using equal_range, like:
auto p = category_search.equal_range("hello");
This will put the beginning of the range in p.first and the end in p.second.
The second in the pair is a vector<int>, so you construct a vector with 1 element initialized with 5 instead of push_back.
categorySearch.insert(make_pair("hello", std::vector<int>(1,5)));
Live example: http://ideone.com/JlMUuN
If the item already exists, use the return value that unordered_map::insert gives you, which is a std::pair consisting of the iterator and a bool denoting the success of the insertion:
#include <vector>
#include <unordered_map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
typedef unordered_map<string, vector<int>> CMap;
CMap category_map;
category_map.insert(make_pair("hello", std::vector<int>(1, 5)));
auto pr = category_map.insert(make_pair("hello", std::vector<int>(1, 5)));
if (!pr.second)
pr.first->second.push_back(10);
cout << pr.first->second.size();
}
Live Example: http://ideone.com/svEqcD
You will see that the map entry for "hello" has now 2 items in the vector, since the second call to insert failed due to item previously existing.
See here: http://en.cppreference.com/w/cpp/container/unordered_map/insert
Related
I am trying to define a map from member function pointers to strings in C++. But the the compiler complains that operator < is not defined for member function pointers. Is there a way to do this?
Example code:
#include <map>
#include <string>
class foo;
typedef void (foo::*fooFunc)();
class foo {
public:
foo() {
myMap[func]="example function";
}
void func() {};
std::map<fooFunc,std::string> myMap;
};
Resulting error message:
In file included from C:/msys64/mingw64/include/c++/6.2.0/bits/stl_tree.h:65:0,
from C:/msys64/mingw64/include/c++/6.2.0/map:60,
from grr.cpp:1:
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h: In instantiation of 'constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = void (foo::*)()]':
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_map.h:501:32: required from 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = void (foo::*)(); _Tp = std::__cxx11::basic_string<char>; _Compare = std::less<void (foo::*)()>; _Alloc = std::allocator<std::pair<void (foo::* const)(), std::__cxx11::basic_string<char> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::__cxx11::basic_string<char>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = void (foo::*)()]'
grr.cpp:9:15: required from here
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h:386:20: error: invalid operands of types 'void (foo::* const)()' and 'void (foo::* const)()' to binary 'operator<'
{ return __x < __y; }
~~~~^~~~~
std::map requires a comparison operator for its keys. By default, it uses operator <. Since operator < is not defined for pointer-to-member types, you are getting this error.
To fix it you'll need to define a custom comparison operator and provide it as a parameter to std::map. Alternatively, use a different container.
From cppreference:
std::map is a sorted associative container that contains key-value
pairs with unique keys. Keys are sorted by using the comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Maps are usually implemented as red-black
trees
If you want options on how to write such a comparison operator, it has already been answered here.
I have a complex map that, in the end, stores pointers to Drawable objects. Drawable objects have a draw() member function which is declared as const. I need to call all the draw functions for all objects stored in my map that are of a certain type, and I must do it inside a const function. However I can't seem to be able to preserve the const-correctness of my function (drawSolid).
My outer map (map<int, ##>) is essentially indexing some sub-maps. Sub-maps are, in turn, indexing vectors (map<ItemType, vector<##> >). Finally, this vector keeps a set of shared_ptr<Drawable> objects.
If I remove the const qualifier from my function header, everything compiles, but I need it to be const. How may I iterate through my multidimensional map, preserving const-correctness?
void DrawableItems::drawSolid(int item_list = -1) const
{
typedef std::map<int, std::map<ItemType, std::vector<std::shared_ptr<Drawable> > > > drawablemap;
std::vector<std::shared_ptr<Drawable> > its;
for(drawablemap::const_iterator li = __items.begin(); li != __items.end(); li++) {
its = li->second[SOLID];
for(auto di = its.begin(); di != its.end(); di++) {
di->get()->draw();
}
}
}
This the error I get from the compiler (G++):
/.../dss-sim/src/graphics/DrawableItems.cpp: In member function ‘void DrawableItems::drawSolid(int) const’:
/.../dss-sim/src/graphics/DrawableItems.cpp:51:35: error: passing ‘const std::map<DrawableItems::ItemType, std::vector<std::shared_ptr<Drawable> > >’ as ‘this’ argument discards qualifiers [-fpermissive]
its = li->second[SOLID];
^
In file included from /usr/include/c++/5/map:61:0,
from /.../dss-sim/src/common/dss.hpp:11,
from /.../dss-sim/src/graphics/DrawableItems.hpp:19,
from /.../dss-sim/src/graphics/DrawableItems.cpp:15:
/usr/include/c++/5/bits/stl_map.h:494:7: note: in call to ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = DrawableItems::ItemType; _Tp = std::vector<std::shared_ptr<Drawable> >; _Compare = std::less<DrawableItems::ItemType>; _Alloc = std::allocator<std::pair<const DrawableItems::ItemType, std::vector<std::shared_ptr<Drawable> > > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::vector<std::shared_ptr<Drawable> >; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = DrawableItems::ItemType]’
operator[](key_type&& __k)
There is no const version of operator[] in std::map. However, there is a const version of at() which you can use instead:
its = li->second.at(SOLID);
The reason is that operator[] inserts an element if there is no element yet, so there can not be a const version of operator[].
at() on the other hand throws an exception if no element exists, and this is compatible with a const std::map.
This question already has answers here:
Why isn't the [] operator const for STL maps?
(6 answers)
Closed 8 years ago.
I try to return a specific object of my std::map as follows :
const Vertex& Graph::getVertex(const std::pair<size_t, size_t>& pos) const // -> compile error
{
return this->_vertices[std::get<0>(pos)][std::get<1>(pos)];
}
Map :
std::map<size_t, std::vector<Vertex>> _vertices;
However, I get a compilation error if I let the constness of my getVertex function. Does it mean that accessing my map elements this way actually modify its instance ? Is there a better way to access my map elements ?
The error :
error: passing ‘const std::map<unsigned int, std::vector<Vertex> >’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::o\
perator[](const key_type&) [with _Key = unsigned int; _Tp = std::vector<Vertex>; _Compare = std::less<unsigned int>; _Alloc = std::allocator<std::pair<const unsigned int, std::vector<Vertex> > >; std::map\
<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::vector<Vertex>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = unsigned int]’ discards qualifiers [-fpermissive]
getVertex is const member function
this->_vertices[std::get<0>(pos)][std::get<1>(pos)]; can modify the _vertices map using std::map::operator[] when key doesn't exists , hence the error
I am trying to calculate the shortest paths between a graph using BGL. The function I am calling is
boost::johnson_all_pairs_shortest_paths(g,distances);
where g is a custom graph type
typedef property<label_t, int> LabelProperty;
typedef property<edge_weight_t,int> EdgeWeightProperty;
//graph definition
typedef adjacency_list<listS,
listS,
directedS,
property<vertex_index_t,int,
LabelProperty >,
EdgeWeightProperty
> Graph;
and I am implementing the distances as a map of maps
typedef map<int,map<int,int> > DistMat;
DistMat distances;
so I can access distaces as distances[i][j] and can work with large graphs.
When I compile (with gcc 4.6.3), I get the following error:
/usr/include/boost/graph/graph_concepts.hpp:518:31: error: passing ‘const std::map >’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int, _Tp = std::map, _Compare = std::less, _Alloc = std::allocator > >, std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::map, std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’ discards qualifiers [-fpermissive]
Indeed if I use the [-fpermissive] option the code compiles and works. However, I would like to arrange the code so it compiles without selecting this option. I believe that the solution is here, but I am quite lost among so many templates.
Any hints? Thank you very much in advance
typedef struct
{
pthread_t threadId;
int acceptSocketD;
char *message;
} threadData;
map <unsigned int, threadData> serverPortNumberThreadId;
map <unsigned int, threadData> :: iterator serverPortNumberThreadIdIter;
usage:
threadData obj;
obj.threadId = 0;
obj.acceptSocketD = 0;
obj.message = "Excuse Me, please!";
serverPortNumberThreadId.insert (3490, obj);
error:
error: no matching function for call to ‘std::map<unsigned int, threadData>::insert(int, threadData&)’
/usr/include/c++/4.5/bits/stl_map.h:500:7: note: candidates are: std::pair<typename std::map<_Key, _Tp, _Compare, _Alloc>::_Rep_type::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::map<_Key, _Tp, _Compare, _Alloc>::value_type&) [with _Key = unsigned int, _Tp = threadData, _Compare = std::less<unsigned int>, _Alloc = std::allocator<std::pair<const unsigned int, threadData> >, typename std::map<_Key, _Tp, _Compare, _Alloc>::_Rep_type::iterator = std::_Rb_tree_iterator<std::pair<const unsigned int, threadData> >, std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair<const unsigned int, threadData>]
/usr/include/c++/4.5/bits/stl_map.h:540:7: note: std::map<_Key, _Tp, _Compare, _Alloc>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::insert(std::map<_Key, _Tp, _Compare, _Alloc>::iterator, const std::map<_Key, _Tp, _Compare, _Alloc>::value_type&) [with _Key = unsigned int, _Tp = threadData, _Compare = std::less<unsigned int>, _Alloc = std::allocator<std::pair<const unsigned int, threadData> >, std::map<_Key, _Tp, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const unsigned int, threadData> >, std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair<const unsigned int, threadData>]
tcpClient.cpp: In function ‘int main(int, char**)’
You need to insert the value into map:
serverPortNumberThreadId.insert ( std::make_pair( 3490, obj) );
For other ways to insert into map, see the map::insert() reference page.
You need a pair for the insert function, because in common with other containers insert takes the value_type, which in the case of map is a pair -- each entry has a key and a mapped value. The value_type represents one entry in the container and hence includes both.
You could write serverPortNumberThreadId[3490] = obj; instead, if you prefer the look of it. Behavior is sometimes different (insert does nothing if the key already exists, whereas this code overwrites it, but unless you're relying on that behavior of insert already this makes no difference to you). Performance might be slightly different in terms of the number of threadData objects created/copied/assigned.
//always overwrites the old value
serverPortNumberThreadId[3490]=obj;
//only insert a new one
serverPortNumberThreadId.insert(std::make_pair(3490, obj));
You have to send a pair to insert function and not key,value.
mymap.insert ( pair<unsigned int,threadData>(3490, obj) );