C++ Maps, How these two declarations are not correct? - c++

I am using map to store elements and I need to allocate a dynamic map memory such that I can pass this memory to caller function without any hassle however the following code snippet is confusing me. Have a look here.
std :: map <int, int> map1;
map1[some_integer_x] = some_integer_y; // OK at compilation.
But following snippet is not OK and gives error.
std :: map <int, int> * map2 = new std :: map <int, int>
map2[some_integer_x] = some_integer_y; // Not OK at compilation.
Here is the error it gives me on GCC 5.
error: no match for ‘operator=’ (operand types are ‘std::map<int, int>’ and ‘int’)
map2[some_integer_x] = some_integer_y;
I can understand that this is related to some pointers issue. What can be reason? What are my other options? I want to pass map without involving a copy as map will contain huge amount of information.

map2 is a pointer to map.
To access the std::map::operator[] you need to dereference the pointer first, using the operator *.
Because [] has higher operator precedence than *, you need to write
(*map2)[some_integer_x] = some_integer_y;

Since map2 is a pointer, then operator[] works the same way as for an array of std::map<int, int>.
The line :
map2[some_integer_x] = some_integer_y;
Means "go to the element some_integer_x and assign the to it value some_integer_y".
That causes the compilation error of mismatched type, because
type of map2[some_integer_x] is std::map<int, int> you are trying to assign an integer value to it.
So, the first of all you need to deference the pointer:
(*map2)[some_integer_x] = some_integer_y;

You are operating on the pointer to a map as if it were an instance of a map. You need to dereference the pointer before use like in the example below:
#include <map>
#include <iostream>
int main() {
std :: map <int, int> map1;
map1[1] = 2;
std::map<int, int> *map2 = new std::map<int, int>();
(*map2)[1] = 3;
std::cout << "Map1 at location 1 is: " << map1[1] << std::endl; //Prints 2
std::cout << "Map2 at location 1 is: " << (*map2)[1] << std::endl; // Prints 3
delete map2;
}

Related

How to insert key value pairs to new unordered_map<int, int>* (pointer) in C++?

I have an unordered_map pointer in a structure since I need it in shared memory to access and modify as the program runs.
struct Umaps {std::unordered_map<int, int> *node_index;} ;
I then initialize the unordered_map, node_index, in another function.
Umapsptr->node_index = new std::unordered_map<int, int>();
Where Umapsptr is the pointer to the Umaps structure in my function. I then try to insert values to the unordered_map.
Umapsptr->node_index[5] = 10;
But I get this error and I don't know how to resolve it:
error: no match for ‘operator=’ (operand types are ‘std::unordered_map<int, int>’ and ‘int’)
Umapsptr->node_index[5] = 10;
What am I doing wrong? Any help would be incredibly appreciated.
Applying [] to a pointer does pointer arithmetic. You want to apply the [] to the pointed at object, so you need to explicitly dereference the pointer:
(*Umapsptr->node_index)[5] = 10;
If you know the key 5 is already in the map, you can use at instead:
Umapsptr->node_index->at(5) = 10;
but this will throw an exception if the key is not present. If you know the key is not present, you can use emplace:
Umapsptr->node_index->emplace(5, 10);
but this will do nothing if the key is already present with a different value.

c++ returning dereference of a pointer causes Segmentation fault

I have a map pointer m, which is not a local variable of the function getMap, getMap returns a dereference of pointer m, and when I run the code below, I got Segmentation fault (core dumped), why returning dereference of a pointer causes Segmentation fault.
std::map<int, std::string> *m;
const std::map<int, std::string>& getMap(){
m->insert(std::make_pair<int, std::string>(0, "hi"));
return *m;
}
int main(){
const std::map<int, std::string>& m = getMap();
std::cout << "length: " << m.size() << std::endl;
}
m is an uninitialized global pointer. The compiler gives it a default value of nullptr. So you just invoke undefined behaviour when you dereference it - it is likely that you try to read or write memory at a zero address which causes the segmentation fault.
How to fix:
define a std::map object and make m point there
std::map<int, std::string> glob_map;
std::map<int, std::string> *m = &glob_map;
... remaining of code unchanged
make GetMap return a temporary
const std::map<int, std::string> getMap(){
std::map<int, std::string> localmap
localmap.insert(std::make_pair<int, std::string>(0, "hi"));
return m;
}
int main(){
const std::map<int, std::string>& m = getMap();
std::cout << "length: " << m.size() << std::endl;
}
That is a rather advanced method, since you use the fact that the lifetime of a temporary is extended when it is directly affected to a reference. The normal way would be to copy the temporary to a plain object (and let the optimizing compiler elide the unnecessary copy):
const std::map<int, std::string> m = getMap();
You did not initialize m at all and dereference on it is UB. You need to new (and delete) it.
If you don't have to use raw pointer, then don't use.
std::map<int, std::string> m;
const std::map<int, std::string>& getMap(){
m.insert(std::make_pair<int, std::string>(0, "hi"));
return m;
}
You just create and allocate a single pointer to a std::map, but not the map itself. Hence m points to nowhere, and dereferencing it is undefined behaviour.
You probably want something like
std::map<int, std::string> *m = new map<int, std::string>();
There's not problem with the code you've shown (other than that you never initialize m).
Probably the issue is somewhere else in the code.
The most likely issues are:
You call getMap before m is initialized.
You call getMap after m has been destroyed.
Use something like valgrind to identify which one it is and get more information about the problem.

C++ maps, vectors, pairs with references

I am trying to share references between a map and another vector as in the following code:
template <typename T>
void f(T& x) {
std::map<T, vector<int> > mp;
mp[x] = vector<int>(2, 4);
vector< pair<T&, int> > v1;
for (auto& kv : mp) {
v1.push_back( make_pair(kv.first, 0) );
}
}
and call this function from another
int x = 2;
f(x);
But this does not compile.
error: no matching function for call to ‘std::vector<std::pair<int&, int>, std::allocator<std::pair<int&, int> > >::push_back(std::pair<int, int>)’
v1.push_back( make_pair(kv.first, 0) );
Does anybody see why?
EDIT
I was able to make things work with
std::reference_wrapper
Here is my current solution.
template <typename T>
void f(T& x) {
std::map<std::reference_wrapper<T>, vector<int> > mp;
mp[x] = vector<int>(2, 4);
vector< pair<T&, int> > v1;
for (auto& kv : mp) {
v1.push_back( make_pair( std::ref(kv.first) , 0) );
}
v1[0].first = 34;
}
And
int x = 2;
f(x);
cout << x << endl;
prints 34. Do people see any cons with this vs the pointer based approach?
In general you can store std::pair<T&,int> in an std::vector, but you have to insert values correctly.
make_pair(x, 0) creates a std::pair<T,int> with no reference.
You can for example use an initializer lists instead:
v.push_back({x,0});
Another method would be using std::ref:
v.push_back(std::make_pair(std::ref(x),0));
Example:
std::vector<std::pair<int&,int>> v;
int a = 1;
v.push_back({a,a});
v.push_back(std::make_pair(std::ref(a),a));
a = 2;
std::cout << v[0].first << " " << v[0].second << std::endl; // will write "2 1"
However for your purpose it will not work. You should not get references to keys of a std::map. A std::map is a sorted container, you can not just change a key from the outside. This would mess up the inner workings of the std::map.
Because of type mismatch:
vector< pair<T&, int> > v1; // value_type is pair<int&, int>
// ^^^^^^^^^^^^^
and
v1.push_back( make_pair(kv.first, 0) );
// ^^^^^^^^^^^^^^^^^^^^^^ pair<int, int>
A lvalue-reference to int cannot be initialized with an rvalue.
You cannot have a container of references.
A reference must be initialized at the point of declaration, and declaring a container of references implies filling that container later on.
You could use pointers instead. Non (memory) owning pointers are considered valid c++ style.
Second option would be to look into
std::reference_wrapper
If you really want to reference the same object in your map and your vector of pairs, then you'd be better off looking at a pointer based solution. If your original type is stack based then your secondary collection could use a raw pointer - since it does not have ownership of the key object and so should not take any role in retaining or destructing it - to deference it. Although you'll have to be careful about managing the two collections in sync to avoid any dangling pointers.
That said, depending on why you want to do it, if it's a simple int it may not be worth the complexity.

alias used not modifying the actual contents of STL container

I am unable to understand the following behaviour. When I use currPMap to modify the value, the value at the actual location is not modified. Why is that so.
I checked with the reference the operator[] and at() return reference and so this should have worked.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
typedef map<int, int> intMap;
typedef map<int, int>::iterator mapIt;
int main(void) {
vector< map<int, intMap > > b(2);
int curr=0, next=1;
map<int, intMap> currPMap = b.at(curr);
(currPMap[4])[2] = 3; //modified by currPMap.
cout<<((b.at(curr))[4])[2]<<endl;
((b.at(curr))[4])[2] = 3; //modified using the actual vector.
cout<<((b.at(curr))[4])[2]<<endl;
}
Output:
0
3
P.S.: I know what I am doing here can be achieved by many other ways in this setting, but this is not the actual program. This is just the explicit version of the problem that I am facing with my code. I would be grateful if someone answers what is wrong in this method.
Because you are getting a copy of a map here, not an alias:
map<int, intMap> currPMap = b.at(curr); // currMap is a copy of b[0]
You then modify the copy, not the map stored in the vector.
What you need is a reference:
map<int, intMap>& currPMap = b.at(curr); // currMap refers to b[0]
map<int, intMap> currPMap = b.at(curr);
That's not an alias (aka reference); that's a copy. If you want a reference, you need to declare it thusly:
map<int, intMap> & currPMap = b.at(curr);
^
Beware that the reference may be invalidated if you add or remove elements to the vector, since vectors need to move their elements to maintain a contiguous array.

Pointer to a map

If I define a pointer-to-map like this:
map<int, string>* mappings;
mappings is a pointer. How should I use this pointer to operate the map?
Use the pointer just like you use any other pointer: dereference it to get to the object to which it points.
typedef std::map<int, string>::iterator it_t;
it_t it1 = mappings->begin(); // (1)
it_t it2 = (*mappings).begin(); // (2)
string str = (*mappings)[0]; // (3)
Remember that a->b is — mostly — equivalent to (*a).b, then have fun!
(Though this equivalence doesn't hold for access-by-index like (*a)[b], for which you may not use the -> syntax.)
Not much difference except that you have to use -> for accessing the map members. i.e.
mapping->begin() or mapping->end()
If you don't feel comfortable with that then you can assign a reference to that and use it as in the natural way:
map<int, string> &myMap = *mappings; // 'myMap' works as an alias
^^^^^^^^
Use myMap as you generally use it. i.e.
myMap[2] = "2";
myMap.begin() or myMap.end();
For instance:
#include <map>
#include <string>
#include <iostream>
int main() {
std::map<int, std::string> * mapping = new std::map<int, std::string>();
(*mapping)[1] = "test";
std::cout << (*mapping)[1] <<std::endl;
}
With the introduction of "at" function in c++ 11, you can use mappings->at(key) instead of (*mapping)[key].
Keep in mind that this api will throw out_of_range exception if the key is not already available in the map.
another nice way of using pointers, which I like is calling mappings->(and the function you want to call)
Well, STL is designed to reduce the complexity of pointer handling..so better approach is to use stl::iterator.. try to avoid pointers :-/