What is the difference between the index overloaded operator and the insert method call for std::map?
ie:
some_map["x"] = 500;
vs.
some_map.insert(pair<std::string, int>("x", 500));
I believe insert() will not overwrite an existing value, and the result of the operation can be checked by testing the bool value in the iterator/pair value returned
The assignment to the subscript operator [] just overwrites whatever's there (inserting an entry if there isn't one there already)
Either of the insert and [] operators can cause issues if you're not expecting that behaviour and don't accommodate for it.
Eg with insert:
std::map< int, std::string* > intMap;
std::string* s1 = new std::string;
std::string* s2 = new std::string;
intMap.insert( std::make_pair( 100, s1 ) ); // inserted
intMap.insert( std::make_pair( 100, s2 ) ); // fails, s2 not in map, could leak if not tidied up
and with [] operator:
std::map< int, std::string* > intMap;
std::string* s1 = new std::string;
std::string* s2 = new std::string;
intMap[ 100 ] = s1; // inserted
intMap[ 100 ] = s2; // inserted, s1 now dropped from map, could leak if not tidied up
I think those are correct, but haven't compiled them, so may have syntax errors
For a map, the former (operator[]) expression will always replace the value part of the key-value pair with the new supplied value. A new key-value pair will be inserted if one doesn't already exist.
In contrast, insert will only insert a new key-value pair if a key-value pair with the supplied key part does not already exist in the map.
In addition to the fact that map::operator[] will replace an existing value is that operator[] map::will create and add to the map a default existing value to replace before the replacement occurs (the map::operator[]() call has to return a reference to something). For items that are expensive to create this could be a performance issue.
See "Item 24: Choose carefully between map::operator[] and map::insert when efficiency is important" in Scott Meyers' Effective STL.
The insert method inserts into the map, while the overloaded index operator will return the element with the key key_value if it is in the map, if it is not already in the map then it will insert it.
Just to add to Michael Burr's answer, the book you should be looking for is Scott Meyer's <Effective STL> not the <Effective C++>, as Michael has linked it wrongly.
the expression
m[k] = v;
checks to see if the key k is already in the map. If not, it’s
added, along with v as its corresponding value. If k is already in the
map, its associated value is updated to v.
(when adding new k-v)
We first default-construct a <value obj>, then we immediately assign
it a new value. If it’s measurably more efficient to construct a
<value obj> with the value we want instead of default-constructing the
<value obj> and then doing the assignment, we’d be better off replacing our
use of operator[] (including its attendant construction plus
assignment) with a straightforward call to insert:
(updating existing k's v)
we now understand that when an “add” is performed, insert is more
efficient than operator[]. The situation is reversed when we do an
update, i.e., when an equivalent key is already in the
map.
Related
I have a container that looks like this:
std::map<std::string, std::vector<double>> id_values;
I will iterate through other pairs of doubles and strings, and I want to add a new element to the map if it doesn't exist, or append to the vector if it does. Is there a more succinct solution than the following?
auto loc = id_values.find(key);
if (loc != id_values.end()) {
loc->second.push_back(val);
} else {
loc.insert({key, {val}});
}
I suppose I could do a ternary operator but I feel that will make the code less readable, I'm more wondering if there is a better pattern for what I'm trying to achieve rather than conditional.
You can just use operator[]. It will return a reference to the object with the specified key if it is in the map, or if the lookup fails, it will create a new value-initialized object (an empty vector in your case) and return a reference to the new object.
id_values[key].push_back(val);
Alternatively, if you need to use different constructor arguments instead of default-constructing the mapped_type, you can use try_emplace (or regular emplace if you can't use C++17):
auto [itr, inserted] = id_values.try_emplace(key, vector_constructor_args...);
itr->second.push_back(val);
Hello I think what you want to do is ether to insert an element in your container using the method insert or the operator[].
By using the operator [] what you want to do is simply:
for(auto it: map1)
if(map2.find(it.first) == map2.end() )
map2.insert(map2.end(),it);
In the code below:
#include <map>
#include <utility>
#include <iostream>
using namespace std;
int main(){
pair<int,int> p1(1,1);
pair<int,int> p2(1,2);
map<int,int> m;
m.insert(p1);
m.insert(p2);
cout << "Map value: "<< m.at(1) << endl;
}
It printed out : Map value: 1, why m.insert(p2) doesn't overwrite the previous entity in the map?
map.insert() only inserts if the container doesn't already contain an element with an equivalent key.
You should use operator[] instead:
m[p2.first] = p2.second;
In the std::map::insert reference it is said that:
Inserts element(s) into the container, if the container doesn't already contain an element with an equivalent key.
Update as of C++17 There is now the std::map::insert_or_assign() member function:
m.insert_or_assign(p1);
As the name suggests, if the key is already present then the value is assigned (and the key object kept) rather than erasing and freshly copy constructing the key and value. (So it's equivalent to the first of the two pre-C++17 snippets below.)
If you want an iterator pointing at the (new or updated) element, you again need to pick the value out of the returned pair. Since you're using C++17, you can now use a structured binding:
auto [it, wasInserted] = m.insert_or_assign(p1);
Before C++17 Putting together the other answers, if you want to avoid the assumption of being default constructable you get insert-with-overwrite code that looks like this:
auto itAndWasInserted = m.insert(p1);
if (!itAndWasInserted.second) {
*(itAndWasInserted.first) = p1;
}
In the above snippet, if the element is already present then the new value is assigned to it. That's usually what you want. If you instead want to construct rather than assign the new value, but still want to avoid a second seek (after you've erased the original value), you end up with this monster:
auto itAndWasInserted = m.insert(p1);
auto it = itAndWasInserted.first;
if (!itAndWasInserted.second) {
auto afterIt = m.erase(it);
auto newItAndWasInserted = m.insert(afterIt, p1); // Hint form of insert
it = newItAndWasInserted.first;
}
At the end of the code block, it is an iterator pointing at the just-inserted element.
Realistically, in most cases you probably just want to use yizzlez's suggestion of operator[], but I thought it would be good to note the theoretically best answer.
It doesn't overwrite. However if you check the return value, there is a std::pair<iterator, bool>. If bool is true, then it was inserted. If the bool is false, then it was not inserted because of a collision. At that point, you can then overwrite the data yourself by writing to the iterator.
This is supposed to happen. map.insert() will only insert elements into the container if it doesn't already contain any elements, so this will ignore the later value elements assigned to it.
let's say I have a map whose key is a pair and whose custom comparator guarantees unicity against the first element of that pair.
class comparator
{
public:
bool operator()(const std::pair<std::string, std::int>& left,
const std::pair<std::string, std::int>& right)
{
return left.first < right.first;
}
};
std::map<std::pair<std::string, std::int>, foo, comparator>;
Now I'd like this map to be more intelligent than that, if possible.
Instead of being rejected at insertion time in case a key with the same string as first element of the pair already exists, I'd to overwrite the "already existing element" if the pair's integer (.second) of the "possibly going to be inserted element" is bigger.
Of course I can do this by looking in to the map for the key, getting the key details and overwriting it if necessary.
Alternatively I could adopt a post-insertion approach with a multimap on top of which I would iterate to clean up duplicates keeping just the key with the biggest pair integer.
The question is : can I do that natively by overriding part of the stl implementation ([] operator - insert method) or improving my custom comparator and then simply relying on map's insert method ?
I don't know if this is accepted but we could imagine having a non const comprator which would be able of updating the already stored (key, value) pair under certain circumstances.
ValueThe answer to your question is that you cannot do it.
There are two problems with your proposed implementation:
The keys must remain const as they are the index for the map
Independent of what the comparator did to the elements it is comparing the std::map would still insert the item before or after left based on the return of the comparator
The solution to the problem is as suggested by #MvG. Your key should not be paired, it is your value that should be paired.
This has the added benefit that you don't need a custom comparator.
The problem is that you will need a custom inserter:
std::pair< int, foo >& tempValue = _myMap[ keyToInsert ];
if( valueToInsert.first >= tempValue.first )
{
tempValue = valueToInsert;
}
Note that this will only work if all the valueToInsert.firsts that you use are positive, cause the default constructor for an int is 0. If you had negative valueToInsert.firsts the default constructed value pair would be inserted instead of your element.
map<string, string> dada;
dada["dummy"] = "papy";
cout << dada["pootoo"];
I'm puzzled because I don't know if it's considered undefined behaviour or not, how to know when I request a key which does not exist, do I just use find instead ?
The map::operator[] searches the data structure for a value corresponding to the given key, and returns a reference to it.
If it can't find one it transparently creates a default constructed element for it. (If you do not want this behaviour you can use the map::at function instead.)
You can get a full list of methods of std::map here:
http://en.cppreference.com/w/cpp/container/map
Here is the documentation of map::operator[] from the current C++ standard...
23.4.4.3 Map Element Access
T& operator[](const key_type& x);
Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.
Requires: key_type shall be CopyConstructible and mapped_type shall be DefaultConstructible.
Returns: A reference to the mapped_type corresponding to x in *this.
Complexity: logarithmic.
T& operator[](key_type&& x);
Effects: If there is no key equivalent to x in the map, inserts value_type(std::move(x), T()) into the map.
Requires: mapped_type shall be DefaultConstructible.
Returns: A reference to the mapped_type corresponding to x in *this.
Complexity: logarithmic.
If you try to access a key value using index operator [], then 2 things can happen :
The map contains this key. So it will return the corresponding key value.
The map doesn't contain the key. In this case, it will automatically add a key to the map with null value.
"pootoo" key does't exist in your map. So it will automatically add this key with value = ""(empty string). And your program will print empty string.
Here map size will increase by 1.
To search a key you can use map_name.find(), which will return map_name.end() if the key doesn't exist. And no extra key will be added.
You can use [] operator when you want to set value for a key.
It's not undefined behavior. If operator [] doesn't find a value for the provided key, it inserts one at that position.
For operator[], if you try to access a value for a key that doesn't exist, a new value object that has been default constructed will be put into the map and it's reference returned.
The operator[] for map returns a non-const reference and you can assign using that in the way you've shown on your second line. Accessing in this way will create a default contructed element of value type.
If you want to find a find an element, a better way is
iterator find ( const key_type& x )
(or the const alternative) which will return an iterator equal to <map>.end() if it doesn't find the key, or if you just want to know if it's in the collection you can use
size_type count ( const key_type& x ) const
which will always return either 1 or 0 for a map since keys are unique.
If operator [] doesn't find a value for the provided key, it inserts one at that position.
But you should note that if you visit a not exist key and invoke it's member function, like mapKV[not_exist_key].member_fun().The program may crash.
Let me give an example, test class as below:
struct MapValue{
int val;
MapValue(int i=0){
cout<<"ctor: "<<i<<endl; val = i;
}
~MapValue(){
cout<<"dtor: "<<val<<endl;
}
friend ostream& operator<<(std::ostream& out, const MapValue& mv){
cout<<"MapValue: "<<mv.val<<endl;
}
string toString(){
cout<<"MapValue: "<<val<<endl;
}
};
Test code:
cout<<"-------create map<int, MapValue>-------"<<endl;
map<int, MapValue> idName{{1, MapValue(1)}, {2, MapValue(2)}};
cout<<"-----cout key[2]-----"<<endl;
cout<<idName[2]<<endl;
cout<<"-----cout key[5]-----"<<endl;
cout<<idName[5]<<endl;
cout<<"------- runs here means, does't crash-------"<<endl;
Output as below:
-------create map<int, MapValue>-------
ctor: 1
ctor: 2
dtor: 2
dtor: 1
dtor: 2
dtor: 1
-----cout key[2]-----
MapValue: 2
-----cout key[5]-----
ctor: 0
MapValue: 0
-------runs here means, does't crash-------
dtor: 0
dtor: 2
dtor: 1
We can see that: idName[5] invoke map construct {5, MapValue(0)} to insert to idName.
But if, you invoke member function by idName[5], then the program crashes :
cout<<"-------create map<int, MapValue>-------"<<endl;
map<int, MapValue> idName{{1, MapValue(1)}, {2, MapValue(2)}};
idName[5].toString(); // get crash here.
cout<<"------- runs here means, doesn't crash-------"<<endl;
please have a look at the out_of_range exception:
http://www.cplusplus.com/reference/stdexcept/out_of_range/
this is what map::at and map::operator[] will throw if key does not exist. You can catch it the same way as the vector example in the link.
You can also use:
http://www.cplusplus.com/reference/map/map/find/
And check against map::end
A while ago, I had a discussion with a colleague about how to insert values in STL maps. I preferred map[key] = value; because it feels natural and is clear to read whereas he preferred map.insert(std::make_pair(key, value)).
I just asked him and neither of us can remember the reason why insert is better, but I am sure it was not just a style preference rather there was a technical reason such as efficiency. The SGI STL reference simply says: "Strictly speaking, this member function is unnecessary: it exists only for convenience."
Can anybody tell me that reason, or am I just dreaming that there is one?
When you write
map[key] = value;
there's no way to tell if you replaced the value for key, or if you created a new key with value.
map::insert() will only create:
using std::cout; using std::endl;
typedef std::map<int, std::string> MyMap;
MyMap map;
// ...
std::pair<MyMap::iterator, bool> res = map.insert(MyMap::value_type(key,value));
if ( ! res.second ) {
cout << "key " << key << " already exists "
<< " with value " << (res.first)->second << endl;
} else {
cout << "created key " << key << " with value " << value << endl;
}
For most of my apps, I usually don't care if I'm creating or replacing, so I use the easier to read map[key] = value.
The two have different semantics when it comes to the key already existing in the map. So they aren't really directly comparable.
But the operator[] version requires default constructing the value, and then assigning, so if this is more expensive then copy construction, then it will be more expensive. Sometimes default construction doesn't make sense, and then it would be impossible to use the operator[] version.
Another thing to note with std::map:
myMap[nonExistingKey]; will create a new entry in the map, keyed to nonExistingKey initialized to a default value.
This scared the hell out of me the first time I saw it (while banging my head against a nastly legacy bug). Wouldn't have expected it. To me, that looks like a get operation, and I didn't expect the "side-effect." Prefer map.find() when getting from your map.
If the performance hit of the default constructor isn't an issue, the please, for the love of god, go with the more readable version.
:)
insert is better from the point of exception safety.
The expression map[key] = value is actually two operations:
map[key] - creating a map element with default value.
= value - copying the value into that element.
An exception may happen at the second step. As result the operation will be only partially done (a new element was added into map, but that element was not initialized with value). The situation when an operation is not complete, but the system state is modified, is called the operation with "side effect".
insert operation gives a strong guarantee, means it doesn't have side effects (https://en.wikipedia.org/wiki/Exception_safety). insert is either completely done or it leaves the map in unmodified state.
http://www.cplusplus.com/reference/map/map/insert/:
If a single element is to be inserted, there are no changes in the container in case of exception (strong guarantee).
If your application is speed critical i will advice using [] operator because it creates total 3 copies of the original object out of which 2 are temporary objects and sooner or later destroyed as.
But in insert(), 4 copies of the original object are created out of which 3 are temporary objects( not necessarily "temporaries") and are destroyed.
Which means extra time for:
1. One objects memory allocation
2. One extra constructor call
3. One extra destructor call
4. One objects memory deallocation
If your objects are large, constructors are typical, destructors do a lot of resource freeing, above points count even more. Regarding readability, i think both are fair enough.
The same question came into my mind but not over readability but speed.
Here is a sample code through which I came to know about the point i mentioned.
class Sample
{
static int _noOfObjects;
int _objectNo;
public:
Sample() :
_objectNo( _noOfObjects++ )
{
std::cout<<"Inside default constructor of object "<<_objectNo<<std::endl;
}
Sample( const Sample& sample) :
_objectNo( _noOfObjects++ )
{
std::cout<<"Inside copy constructor of object "<<_objectNo<<std::endl;
}
~Sample()
{
std::cout<<"Destroying object "<<_objectNo<<std::endl;
}
};
int Sample::_noOfObjects = 0;
int main(int argc, char* argv[])
{
Sample sample;
std::map<int,Sample> map;
map.insert( std::make_pair<int,Sample>( 1, sample) );
//map[1] = sample;
return 0;
}
Now in c++11 I think that the best way to insert a pair in a STL map is:
typedef std::map<int, std::string> MyMap;
MyMap map;
auto& result = map.emplace(3,"Hello");
The result will be a pair with:
First element (result.first), points to the pair inserted or point to
the pair with this key if the key already exist.
Second element (result.second), true if the insertion was correct or
false it something went wrong.
PS: If you don´t case about the order you can use std::unordered_map ;)
Thanks!
A gotcha with map::insert() is that it won't replace a value if the key already exists in the map. I've seen C++ code written by Java programmers where they have expected insert() to behave the same way as Map.put() in Java where values are replaced.
One note is that you can also use Boost.Assign:
using namespace std;
using namespace boost::assign; // bring 'map_list_of()' into scope
void something()
{
map<int,int> my_map = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);
}
Here's another example, showing that operator[] overwrites the value for the key if it exists, but .insert does not overwrite the value if it exists.
void mapTest()
{
map<int,float> m;
for( int i = 0 ; i <= 2 ; i++ )
{
pair<map<int,float>::iterator,bool> result = m.insert( make_pair( 5, (float)i ) ) ;
if( result.second )
printf( "%d=>value %f successfully inserted as brand new value\n", result.first->first, result.first->second ) ;
else
printf( "! The map already contained %d=>value %f, nothing changed\n", result.first->first, result.first->second ) ;
}
puts( "All map values:" ) ;
for( map<int,float>::iterator iter = m.begin() ; iter !=m.end() ; ++iter )
printf( "%d=>%f\n", iter->first, iter->second ) ;
/// now watch this..
m[5]=900.f ; //using operator[] OVERWRITES map values
puts( "All map values:" ) ;
for( map<int,float>::iterator iter = m.begin() ; iter !=m.end() ; ++iter )
printf( "%d=>%f\n", iter->first, iter->second ) ;
}
This is a rather restricted case, but judging from the comments I've received I think it's worth noting.
I've seen people in the past use maps in the form of
map< const key, const val> Map;
to evade cases of accidental value overwriting, but then go ahead writing in some other bits of code:
const_cast< T >Map[]=val;
Their reason for doing this as I recall was because they were sure that in these certain bits of code they were not going to be overwriting map values; hence, going ahead with the more 'readable' method [].
I've never actually had any direct trouble from the code that was written by these people, but I strongly feel up until today that risks - however small - should not be taken when they can be easily avoided.
In cases where you're dealing with map values that absolutely must not be overwritten, use insert. Don't make exceptions merely for readability.
The fact that std::map insert() function doesn't overwrite value associated with the key allows us to write object enumeration code like this:
string word;
map<string, size_t> dict;
while(getline(cin, word)) {
dict.insert(make_pair(word, dict.size()));
}
It's a pretty common problem when we need to map different non-unique objects to some id's in range 0..N. Those id's can be later used, for example, in graph algorithms. Alternative with operator[] would look less readable in my opinion:
string word;
map<string, size_t> dict;
while(getline(cin, word)) {
size_t sz = dict.size();
if (!dict.count(word))
dict[word] = sz;
}
The difference between insert() and operator[] has already been well explained in the other answers. However, new insertion methods for std::map were introduced with C++11 and C++17 respectively:
C++11 offers emplace() as also mentioned in einpoklum's comment and GutiMac's answer.
C++17 offers insert_or_assign() and try_emplace().
Let me give a brief summary of the "new" insertion methods:
emplace(): When used correctly, this method can avoid unnecessary copy or move operations by constructing the element to be inserted in place. Similar to insert(), an element is only inserted if there is no element with the same key in the container.
insert_or_assign(): This method is an "improved" version of operator[]. Unlike operator[], insert_or_assign() doesn't require the map's value type to be default constructible. This overcomes the disadvantage mentioned e.g. in Greg Rogers' answer.
try_emplace(): This method is an "improved" version of emplace(). Unlike emplace(), try_emplace() doesn't modify its arguments (due to move operations) if insertion fails due to a key already existing in the map.
For more details on insert_or_assign() and try_emplace() please see my answer here.
Simple example code on Coliru