How to use a struct pointer in a map? - c++

I want to use std::map to store pointers to a struct by using two ints as the key lookup, e.g.:
std::map<int, int, my_struct*> my_map;
Ideally I would like to write code similar to:
struct my_struct* test1 = get_struct_ptr();
my_map[1, 5] = test1;
Currently I get an error for the my_map definition:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:524:17:
error: called object type 'my_struct *' is not a function or function pointer
Is there a way to do this with std::map or is there another type that can handle this requirement?

A map is always from one type to another. You can create a type to store two ints for keys using std::pair<int, int>, so:
std::map<std::pair<int, int>, my_struct*> my_map;
my_map[{1, 5}] = test1;
Alternatively, you could use a std::tuple<int, int>, a std::array<int, 2>, or a struct with two int data members (but then you'd have to "teach" the map how to compare such structs by withing your own operator<; lots of existing questions & answers go into detail about how to do this).

Related

Why std::pair<const int, int> does not work with some STL containers?

It seems that some containers accept std::pair<const int, int> as a value type, but some do not. The problem is of course in the const part.
I did some googling and found that only std::vector requires copy-assignable data. However, std::pair<const int, int> works just fine with std::vector, std::set and std::list (and perhaps other containers), but not with std::map and std::priority_queue (the latter really bugs me).
The following compiles without problems (gcc 6.1.0)
std::vector<std::pair<const int, int>> vector;
vector.push_back(std::make_pair(3, 5));
std::set<std::pair<const int, int>> set;
set.insert(std::make_pair(3, 5));
std::list<std::pair<const int, int>> list;
list.push_back(std::make_pair(3, 5));
But this results in compilation errors:
std::priority_queue<std::pair<const int, int>> pq;
pq.push(std::make_pair(3, 5));
std::map<int, std::pair<const int, int>> map;
map[2] = std::make_pair(3, 5);
error: assignment of read-only member ‘std::pair<const int, int>::first’
What is the reason behind this? Shouldn't std::map and std::set have the same behavior given that they have the same underlying implementation? Why does it work with std::vector although it requires to move data?
Anything that requires assignment will break.
vector::push_back doesn't require assignment, only copy/move construction.
ditto for set::insert.
ditto for list::push_back.
priority_queue::push needs to move around the existing elements to maintain the heap property (via std::push_heap), which requires assignment.
map[2] = stuff; is literally an assignment. Use map::insert or map::emplace if you want it to work.
Problem
Let's look at your error more deeply:
error: assignment of read-only member ‘std::pair::first’
Solution
There is no solution to this! As you can see here, there is a member that is only in read mode.
std::priority_queue<std::pair<const int, int>> pq;
Here you can see const int. Well, the const modifier makes it so that the variable can be only in read mode and can't be changed! That's your problem here. If you try to assign a constant, well this results in an error because your attempting to change a constant while it is in only READ mode.
References
cpprefrence

No matching member function for call to std::unordered_map::insert()

I tried to insert a pair of values into map that's declared as :
unordered_map <unsigned int, std::string> map;
and when I insert the combination of 2 strings to and an unsigned int to map:
map.insert(std::make_pair(count,str1.append(str2)));
and Xcode say there's "no matching member function for call to 'insert'". I looked at the overload list, and I am not sure which one I should use in this case. Can someone please tell me?
Update: this problem is caused by unsuccesful typedef. I now changed map and defined it as unordered_map<usigned int, string>
The only thing you can insert into
std::unordered_map <unsigned int, objects> map;
is a
std::pair<unsigned int, objects>
You seem to be inserting
std::pair<unsigned int, std::string>
which will not work unless a std::string is implicitly convertible to objects.
If there is a implicit conversion from std::string to objects, then you might want to consider the simpler and possibly more efficient emplace interface:
map.emplace(count, str1.append(str2));

Compare function for pair

I have a pair: typdef pair <unsigned char *, vector<int>> pair_t
I need to implement my own comparison function for that map, so I tried :
struct myCmp
{
int operator()(unsigned char arr_1[10], unsigned char arr_2[10])
{
return memcmp(arr_1, arr_2, 10);
}
};
typdef pair <unsigned char *, vector<int>, **myCmp**> pair_t;
pair_t data(someCharArr, someIntVector);
The error message I get is:
wrong number of template argument (3 should be 2)
I did the same thing with map and it was fine.
How can create my own compare function for pair?
How can I be sure that the pair_t data(someCharArr, someIntVector); will find the correct key (in case of char * as key)?
Thanks.
You seem to be confused about responsibilities of different classes. It's not the std::pair that needs to worry about how to compare pairs together, it's the std::map who needs to worry about how to sort its keys.
typedef std::map<unsigned char*, std::vector<int>, myCmp> map_t;
From how you implement the comparison function and your comment on why you can't use strings, I actually recommend using a std::array instead of a unsigned char *. Your code then looks more like:
typedef std::map<std::array<unsigned char, 10>, std::vector<int> > map_t;
This works because std::array implements all the comparison operators allowing you to use them as keys in a map.
This is going off of the fact that you seem to know the length of your array at compile time, and that it is always 10. If you don't actually know the length of your arrays at compile time, then you would use a std::vector<unsigned char> instead of the std::array.
std::pair is just a struct with two members, It have no comparison object built in.
You may be confused for a comparison object given for std::map (which in its turn, holds sequence of std::pair).

What is difference between const and non const key?

What is the difference between the following two lines?
map<int, float> map_data;
map<const int, float> map_data;
int and const int are two distinct types.
std::map<int, float> and std::map<const int, float> are, similarly, different types.
The difference between std::map<const int, float> and std::map<int, float> is, to a degree, analogous to the difference between, say, std::map<int, float> and std::map<std::string, float>; you get a fresh map type for each.
In the non-const case, the internal key type is still non-const int:
std::map<const int, float>::key_type => const int
std::map<int, float>::key_type => int
However, map keys are semantically immutable, and all map operations that allow direct access to keys (for example, dereferencing iterators, which yields value_type) does constify the key_type:
std::map<const int, float>::value_type => std::pair<const int, float>
std::map<int, float>::value_type => std::pair<const int, float>
So the difference may be largely invisible to you in every way that matters, if your implementation allows it.
That's not always the case, though: the standard officially requires your key type to be copyable and moveable, and some implementations re-use map nodes; under those implementations, attempting to use a const key simply won't work.
The key is already const, so it is redundant to write const in this case. Once an element is entered, its key cannot be changed.
Edit:
As mentioned in the comments, there is difference between the two lines. For example, if you write a function that accepts map<const int, int>, you can't pass to it map<int, int> since they're different types.
But note that although they are different types, they behave the same since the key in a map is a const anyway...
So in conclusion.. The only difference is that they are two different types, you shouldn't care about anything else.
The difference is that the second variant will set the key type for the map as const int. From the "modifiability" point of view this is redundant, since the map already stores its keys as const objects.
However, this can also lead to unexpected and non-obvious differences in the behavior of these two maps. In C++ a template specialization written for type T is different from specialization written for type const T. That means the above two versions of the map might end up using different specializations of various "satellite" templates that depend on the key type. One example is the key comparator predicate. The first one will use std::less<int> while the second one will use std::less<const int>. By exploiting this difference you can easily make these maps to sort their elements in different order.
Issues like that are more obvious with the new C++11 containers like std::unordered_map. std::unordered_map<const int, int> will not even compile, since it will attempt to use a std::hash<const int> specialization for hashing the keys. Such specialization does not exist in the standard library.
const can't be altered once set. And yes as per docs & other answer you should remember that key is const already.
Link: http://www.cplusplus.com/reference/map/map/
Link: http://en.cppreference.com/w/cpp/container/map
While the behaviour of your application will typically be the same, it makes a difference to some compilers you might use. The more specific example of what brought me to this page in the first place:
Explicitly specifying a map as map<const key, value> builds successfully with the gnu toolkit;
However it crashes a Studio12 Solaris x86 build.
map<key, value> builds successfully on both. Behaviour of the application is unchanged.
Const keys can be helpful if the keys are pointers. Using const keys won't let you modify the pointed object when accessing the keys, consider this:
#include <map>
#include <string>
int glob = 10;
int main() {
std::map<const int*, std::string> constKeyMap { { &glob, "foo"} };
std::map<int*, std::string> keyMap { { &glob, "bar" } };
for(const auto& kv : keyMap) { *(kv.first) = 20; }; // glob = 20
for(const auto& kv : constKeyMap) { *(kv.first) = 20; }; // COMPILE ERROR
return 0;
}
const refers to a constant, that, once defined, can't be altered then... non const key is subjected to change... or cant even change, it's just that "no change" is guaranteed in const (once defined), and "change" may or may not occur in non const stuff.

'No matching function for call' error when inserting class into a STL map

I have a STL map in C++ where the key is an unsigned int, and the value is a class whose constructor is:
Foo::Foo(unsigned int integerValue){
//Some stuff
}
At other class I have declarated the std::map at the header:
private:
std::map<unsigned int, Foo> *m_mapFoo;
And at the cpp file I created it and inserted instances of Foo:
m_mapFoo = new std::map<unsigned int, Foo>;
m_mapFoo->insert(0, new Foo(0));
m_mapFoo->insert(1, new Foo(1));
But I'm getting the following error at the insert methods:
no matching function for call to ‘std::map<unsigned int, Foo, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, Foo> > >::insert(const unsigned int&, Foo*)’
Similar problem at find method:
m_mapFoo.find(0)->second->someFunctionIntoFooClass();
Where the error is exactly the following:
request for member ‘find’ in ‘((Foo*)this)->Foo::m_mapGeoDataProcess’, which is of non-class type ‘std::map<unsigned int, Foo, std::less<unsigned int>, std::allocator<std::pair<const unsigned int, Foo> > >*’
Additional notes: I don't have a Foo copy constructor, but I don't think that's the problem.
Any help understanding this errors?
You have a pointer to a map containing Foo values
std::map<unsigned int, Foo> *m_mapFoo;
and you are treating it as if it contained Foo pointer values:
std::map<unsigned int, Foo*> *m_mapFoo;
Try this:
m_mapFoo = new std::map<unsigned int, Foo>;
m_mapFoo->insert(std::make_pair(0, Foo(0)));
m_mapFoo->insert(std::make_pair(1, Foo(1)));
As for the second error, you have a pointer to a map, so you need
std::map<unsigned int, Foo>::iterator it = m_mapFoo->find(0);
if (it) {
it->second.someFunctionIntoFooClass();
} else {
// entry not found
}
Your map is typed to store objects of type Foo, not pointers to objects of type Foo. Seeing as you're trying to initialise the elements with new and access their members via ->, you probably want:
private:
std::map<unsigned int, Foo*> *m_mapFoo;
From the little information you give, it seems unlikely that you need to use pointers anywhere: Try this:
In the header:
private:
std::map<unsigned int, Foo> m_mapFoo;
And at the cpp file:
m_mapFoo[0] = Foo(0);
m_mapFoo[1] = Foo(1);
The semantics of operator[] are slightly different than .insert(). If you need .insert(), use it. But unless you are relying upon the subtle difference, use the more readable operator[].
Similarly at the find method, the expression becomes much more readable if you avoid pointers and use operator[]:
m_mapFoo[0].someFunctionIntoFooClass();