I found a code which use the instruction mymap[1];. Did any one know the meaning of it?
Here is a sample code which compiles successfully:
#include <iostream>
#include <map>
using namespace std;
int main()
{
std::map<int,int> mymap;
mymap[1];
cout<<mymap[1];
return 0;
}
mymap[1] gives you the element with the key 1. For example, you could assign to it: mymap[1] = something;, or print it: std::cout << mymap[1];.
If the element is missing, it's inserted automatically. The new element is value-initialized, which for scalar types essentially means zeroed.
As you noticed, doing just mymap[1]; is allowed. It inserts the element if it's missing, and does nothing else.
Related
In testing it seems to work fine, but I could not find any mention of the expected behaviour in the documentation.
Essentially, if my multi_index_container has 2 ordered_non_unique indices using keys A and B respectively, if I iterate over a range from A and modify the B value (that might cause re-ordering), are the iterators for A invalidated?
Iterators are never invalidated as long as the element is not erased. Please note that invalidation is not the same as repositioning (caused by re-ordering).
Iterators to an index dependent on key A will not be invalidated nor repositioned (i.e., the index keeps its order) upon changes on a different key B, as long as the affected element is not erased (which can happen if the index dependent on key B is unique).
If you want to safely range over an A-index modifying B keys even in the case of erasures, you can do as exemplified below:
Live On Wandbox
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/key.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <iostream>
#include <iterator>
using namespace boost::multi_index;
struct element
{
int a;
int b;
};
using container=multi_index_container<
element,
indexed_by<
ordered_unique<key<&element::a>>,
ordered_unique<key<&element::b>>
>
>;
int main()
{
container c={{0,0},{1,1},{2,2},{3,3},{4,4},{5,5}};
auto print=[](auto& c){
for(const auto& x:c)std::cout<<"{"<<x.a<<","<<x.b<<"}";
std::cout<<"\n";
};
std::cout<<"before: ";
print(c);
for(auto first=c.begin(),last=c.end();first!=last;){
// we get next position now in case first will be invalidated
auto next=std::next(first);
c.modify(first,[](auto& x){
x.b*=2;
});
first=next;
}
std::cout<<"after: ";
print(c);
}
Output
before: {0,0}{1,1}{2,2}{3,3}{4,4}{5,5}
after: {0,0}{3,6}{4,8}{5,10}
Expanded answer: When you're modifying the key of the index you're ranging on, you can either do a first pass to store all the iterators in the range before doing any actual modification (see modify_unstable_range here) or, in case you want to do the thing in just one pass, store the addresses of modified elements along the way to avoid revisitation:
Live On Wandbox
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/key.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <iostream>
#include <iterator>
#include <unordered_set>
using namespace boost::multi_index;
struct element
{
int a;
int b;
};
using container=multi_index_container<
element,
indexed_by<
ordered_unique<key<&element::a>>,
ordered_unique<key<&element::b>>
>
>;
int main()
{
container c={{0,0},{1,1},{2,2},{3,3},{4,4},{5,5}};
auto print=[](auto& c){
for(const auto& x:c)std::cout<<"{"<<x.a<<","<<x.b<<"}";
std::cout<<"\n";
};
std::cout<<"before: ";
print(c);
std::unordered_set<const element*> visited;
for(auto first=c.begin(),last=c.end();first!=last;){
// we get next position now before first is invalidated/repositioned
auto next=std::next(first);
if(c.modify(first,[](auto& x){
x.a*=2; // note we're modifying the key of the index we're at
})){
// element succesfully modified, store address to avoid revisitation
visited.insert(&*first);
}
// move to next nonvisited element
first=next;
while(first!=last&&visited.find(&*first)!=visited.end())++first;
}
std::cout<<"after: ";
print(c);
}
Output
before: {0,0}{1,1}{2,2}{3,3}{4,4}{5,5}
after: {0,0}{6,3}{8,4}{10,5}
I have a set of pairs and I want to change the second field of some pair that I want find first:
#include <iostream>
#include <utility>
#include <set>
int main(){
auto p=std::make_pair(2,3);
std::set<std::pair<int,int>> s{p};
auto it=s.find(p);
it->second=5; // compilation error
if(it!=s.end()) std::cout << it->second << '\n';
}
The above code fails to compile because it complains that the iterator result of find cannot be assigned to. However, I don't see why it is a const iterator.
What is wrong with the above, and how do I assign to the pair that the iterator result of find points to?
It's because set elements shouldn't be mutable, since the red-black tree that is used to implement it logically prevents us from screwing around with the actual nodes. Even without the implementation details, set elements shouldn't be mutable (as pointed out in one of the comments above), or else we could change {2,3} to {2,2}, which would be nonsensical.
Here is an MWE for the unordered map that I'm using. Based on what I understood here, the public member function "erase" is used to erase the elements from the unordered map.
#include <iostream>
#include <string>
#include <algorithm>
#include <unordered_map>
using namespace std;
int main() {
std::unordered_map<int, std::pair<int, int> > m;
m.insert({3, std::make_pair(1,1)});
m.insert({4, std::make_pair(5,1)});
cout << m[4].first << endl;
m.erase(4);
cout << m[4].first << endl;
}
However, what I see from this example is as follows:
5
0
I was expecting only 5 and then throw an error that the key doesn't exist.
What am I missing?
As pointed out in the documentation, if a value does not exist in an unordered_map, operator[] will introduce a new element with the corresponding key. What you get back in the second query is the default-constructed pair, constructed by operator[].
As #you pointed out in his comment, you can instead use at() which includes a "range" check and throws an std::out_of_range exception if the key is now in the unordered_map.
If you want to avoid the exception, you can first check if m.find(4)==m.end(), which indicates that the key 4 is not in the map, as pointed out by #YSC in his comment.
sorry because this question isn't really advanced, but I am having lots of trouble understanding why this program works the way it does.
#include <iostream>
#include <fstream>
#include <set>
#include <vector>
#include <map>
using namespace std;
int main() {
ofstream fout("castle.out");
ifstream fin("castle.in");
map<int, int, greater<int> > cnt;
map<int, int, greater<int> >::iterator x;
x = cnt.begin();
cnt[1] = 0;
cnt[2] = 7;
fout << x->first; //This outputs 2
x++;
fout << x->second;//This outputs 2 again, why not 0?
return 0;
}
So I defined a map called cnt, and then made an iterator for it x. I ordered it by greatest integer to least greatest integer. I set that iterator equal to x.begin(), and then I outputted the value of x, using first. But then I wanted to output the value 0, so I did one x++; and then tried to output the value of x->first. The idea behind this was that the iterator would increase by one and point to the next pair in my map, so then it would point to 1, which comes after the 2.
Why does it not work and give me 2 again?
I realized that if I do this instead:
x++;
x++:
fout << x-> first;
with two x++, I will have the value 1. Why is this? Thanks!
Your code has undefined behavior.
x = cnt.begin();
Sets x to begin() while the container is empty which effectively gives you the end() iterator. Since std::map::operartor[] doesn't invalidate any iterators you still have an end() iterator and dereferencing it is undefined behavior.
I'm trying to use std::map::operator[] to iterate over the values of a std::map with a range-based for loop, but the following doesn't compile:
#include <iostream> // cout, endl
#include <map> // map
#include <set> // set
using namespace std;
int main () {
using namespace std;
const map<int, set<int>> m {{2, {201, 202}}, {3, {301, 302}}};
for (int v : m[2])
cout << v << endl;
return 0;
}
Here's the compiler's error message:
Test.c++:18:19: error: no viable overloaded operator[] for type 'const map<int, set<int> >'
for (int v : m[2])
The followup question is, Given that there are two versions of at(), why aren't there two versions of []?
map::operator[] inserts a new element into the map if the key isn't found, hence it cannot be const, and cannot be called on a const map.
Use m.at(2) instead.
operator[] will do one of two things. It will find the element at that location if it exists and return it. If there is no element at that location, it will value-initialize it, and then return it.
As such, it is neither logically nor programmatically const.
While you may say "but there is an element 2", the constness of an operation depends only on the types of the arguments, not the value of the arguments. And m[int] isn't guaranteed to have a valid element there.
To fix this, you can replace m[2] with m.find(2)->second. This does undefined behavior if 2 is missing from the map, but if 2 is present it will evaluate to the same thing as m[2]. Alterantively, m.at(2) will again do the same thing if 2 is present as a key, and throw an exception if is not present.
As an aside, when working on such a problem, try breaking your code down into smaller pieces. m[2]; all by itself on a line will fail to compile. In fact, the error message told you that it could not find an operator[] -- that might have given you a clue what was wrong.
Your map m is declared const meaning that you can only call const functions on it.
std:map::operator[] is not a such function, since it will modify the map if the specified key is not found.
What you need is std::map::at, which is const so you can call it on your const map.