1

I can't seem to see what I am doing wrong here and getting out of my depth a little.

What I have is some data structures containing vectors and multimaps.

I want to make second vectors/multimaps that contain pointers to the original data so that if I edit the second vector/map the data is change in the original. Reason for this is that the list is a temporary sub-set of the orig based on some criteria.

First I tried this on vectors and it seems to work, here is a working example:

std::vector<std::string> strVect;
strVect.push_back("test1");
strVect.push_back("test2");
strVect.push_back("test3");
std::vector<std::string *>strpVect;
for (std::string &str : strVect)
{
    strpVect.push_back(&str);
}

Here, elements in strpVect point to the originals in strVect.

Now I want to do the same sort of thing for multimaps:

std::multimap<int, std::string> strMap;
strMap.insert(std::pair<int, std::string>(1, "test1"));
strMap.insert(std::pair<int, std::string>(2, "test2"));
strMap.insert(std::pair<int, std::string>(3, "test3"));
std::multimap<int, std::string *>strpMap;
for (std::pair<int, std::string> &val : strMap)  // <<<<< error here
{
    strpMap.insert(std::pair<int, std::string *>(val.first, &val.second));
}

Now when I run this I get the error invalid init of non-const reference of type std::pair....

If I make it a const then it works:

std::multimap<int, std::string> strMap;
strMap.insert(std::pair<int, std::string>(1, "test1"));
strMap.insert(std::pair<int, std::string>(2, "test2"));
strMap.insert(std::pair<int, std::string>(3, "test3"));
std::multimap<int, std::string *>strpMap;
for (const std::pair<int, std::string> &val : strMap)
{
    strpMap.insert(std::pair<int, std::string *>(val.first, &val.second));   // <<<<< error here
}

But then I get an error (more obviously for me) on my insert - but I don't want to insert a const because I want to change the values inside.... I could cast it away, but I see this as a failure of my coding :(

code_fodder
  • 15,263
  • 17
  • 90
  • 167

2 Answers2

6

The key in a (multi)map is const. Thus,

for (std::pair<const int, std::string> &val : strMap)
{
    strpMap.insert(std::pair<int, std::string *>(val.first, &val.second));
}

should do the trick.

Or, even better (imho), use auto:

for (auto &val : strMap)
{
    strpMap.insert(std::pair<int, std::string *>(val.first, &val.second));
}

This automatically gets the const right for you.

Martin Nyolt
  • 4,463
  • 3
  • 28
  • 36
  • Awesome, that explains it nicely, thanks :) ... on a side note, as much as I like the "auto" keywork, it has two annoying side effects: 1. I often don't know what is going on!, 2. code completion stop working (at least in my IDE). ps have to wait 6 more mins to mark it ... :o – code_fodder Sep 27 '16 at 10:53
  • 1
    people can have different opinions on auto. That case here though is one of the prime examples for why to use auto. A `const std::pair<...>&` would still do a copy operation while `auto&` does not. – Hayt Sep 27 '16 at 10:56
  • @Hay I kind of disagree for "this case"... if I had used auto, I would be none-the-wiser. Now I am using the correct type AND I understand it :) - but just in terms of getting it working, I do see your point - so I do upvote it anyway :) – code_fodder Sep 27 '16 at 11:07
  • 1
    There [are](http://stackoverflow.com/q/6434971/1314743) already [several](http://stackoverflow.com/q/6900459/1314743) [discussions](http://programmers.stackexchange.com/q/148289/241913) about the `auto` keyword. ;) I marked the "better" as a personal opinion. – Martin Nyolt Sep 27 '16 at 11:12
  • 2
    for learning and understanding I can get your point. The purpose was meant to be to allow you to not think about it and focus on what you want to do instead of having to keep in mind about the value_type of the map. But it's just a tool to use. You don't have to if you don't want to :) – Hayt Sep 27 '16 at 11:13
  • @MartinNyolt and Hayt - After reading these discussions on auto I think I will just take all of your good points on board and leave it there : ) - thanks both – code_fodder Sep 27 '16 at 11:28
3

The value type of a multimap is pair<const key_type,mapped_type>

so you should be able to use that.

for (std::pair<const int, std::string> &val : strMap)
{
    strpMap.insert(std::pair<int, std::string *>(val.first, &val.second));
}

The reason for not being able to use the non-const version is the following.

The map entries are stored in a way which is sorted by the key. If you change the key afterwards it still stays in it's previous sorted position and then a lookup may result in nothing even when you inserted the data. That is because it looks at the position the key has been inserted but it does not match afterwards if you change the key value after it was inserted in the list.

Hayt
  • 5,210
  • 30
  • 37