Accessing map elements [duplicate] - c++

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

Related

How to define a map from function pointers to strings

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.

How to achieve const-correctness in multidimensional map in C++

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.

STL Unordered Map- inserting into a vector

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

Issue with C++ map using member function pointers as keys [duplicate]

This question already has answers here:
C++ Converting function pointer to unique “hash” key
(2 answers)
Closed 9 years ago.
I am writing in C++, trying to compile under Ubuntu, and I am experiencing some issues with a map using function pointers as keys. When I define the map, I get no compiling errors, but as soon as I try to insert an element, I get a rather wordy
In file included from /usr/include/c++/4.6/string:50:0,
from /usr/include/c++/4.6/bits/locale_classes.h:42,
from /usr/include/c++/4.6/bits/ios_base.h:43,
from /usr/include/c++/4.6/ios:43,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from main.cpp:1:
/usr/include/c++/4.6/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = int (MyClass::*)()]’:
/usr/include/c++/4.6/bits/stl_tree.h:1277:4: instantiated from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = int (MyClass::*)(), _Val = std::pair<int (MyClass::* const)(), std::vector<int> >, _KeyOfValue = std::_Select1st<std::pair<int (MyClass::* const)(), std::vector<int> > >, _Compare = std::less<int (MyClass::*)()>, _Alloc = std::allocator<std::pair<int (MyClass::* const)(), std::vector<int> > >]’
/usr/include/c++/4.6/bits/stl_map.h:518:41: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::map<_Key, _Tp, _Compare, _Alloc>::value_type>::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const value_type&) [with _Key = int (MyClass::*)(), _Tp = std::vector<int>, _Compare = std::less<int (MyClass::*)()>, _Alloc = std::allocator<std::pair<int (MyClass::* const)(), std::vector<int> > >, typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::map<_Key, _Tp, _Compare, _Alloc>::value_type>::other>::iterator = std::_Rb_tree_iterator<std::pair<int (MyClass::* const)(), std::vector<int> > >, std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair<int (MyClass::* const)(), std::vector<int> >]’
main.cpp:36:51: instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:236:22: error: invalid operands of types ‘int (MyClass::* const)()’ and ‘int (MyClass::* const)()’ to binary ‘operator<’
Here is the example that caused the above error message:
#include <iostream>
#include <map>
#include <vector>
// class definition
class MyClass
{
public:
int f1(void);
int f2(void);
};
int MyClass::f1(void)
{
return 1;
}
int MyClass::f2(void)
{
return 2;
}
using namespace std;
int main( int argc, char* argv[] )
{
// define map
map< int (MyClass::*)(void), vector<int> > myMap;
vector<int> myVector;
//myMap[ &MyClass::f1 ] = myVector;
myMap.insert( make_pair( &MyClass::f1, myVector) );
return 0;
}
What could be the issue? I tried with both insert and [] assign, and I get the same error. Browsing the forums, I found this; but could that be the issue? I don't think I need to define an operator "<" for function pointers (shouldn't they behave as regular pointers?) ...or do I?
The error is telling you all you need to know:
invalid operands of types ‘int (MyClass::* const)()’ and ‘int (MyClass::* const)()’ to binary ‘operator<’
You cannot compare member function pointers using standard operator<, so you must provide a custom comparator when declaring your map.
Unfortunately, pointers to member functions cannot be compared for inequality, so you cannot define a comparison operator or use a std::mapin this case. I suggest using std::unordered_map, which only needs a std::hash and equality comparison, which you can do. See here for hashing and here for equality comparison.
You could implement the template specialization for less< int (MyClass::* const)() >, like follows:
typedef int (MyClass::*tMyClassMember)();
namespace std {
template<>
struct less<tMyClassMember>
{
bool operator()(const tMyClassMember& k1, const tMyClassMember& k2) const
{
auto p1 = reinterpret_cast<const intptr_t*>(&k1);
auto p2 = reinterpret_cast<const intptr_t*>(&k2);
return *p1 < *p2;
}
};
}
There may be better ways to compare pointer-to-members than "casting" them to integers, which is an implementation-specific hack, according to this question. That questions contains details about how to do that.

How to pass a structure to a STL map?

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) );