I'm using Google's sparsehashmap, and trying to work out if a value was inserted or looked up. The following works, but obviously it's looking it up twice. How do I do it without the double lookup?
Element newElement = Element();
bool inserted = ((*map).insert(pair<const int64, Element>(key, newElement))).second;
Element element = (*(((*map).insert(pair<const int64, Element>(key, newElement))).first)).second;
if (inserted)
puts("INSERTED");
I can't check the contents of Element (it's a struct) as I want to differentiate between a default Element being found and newElement being inserted. I couldn't work out how to assign ((*map).insert(pair<const int64, Element>(key, newElement))) to a variable as it's of a template type that includes types private to the sparse_hash_map class.
Try this:
typedef sparse_hash_map<...>::iterator sh_iterator; //you already have this, haven't you?
std::pair<sh_iterator, bool> res = map->insert(std::make_pair(key, newElement));
if (res.second)
puts("INSERTED");
If, for whatever reason you don't like the std::make_pair function, you should consider a typedef for the pair type:
typedef pair<const int64, Element> map_pair;
Anyway, the return type of insert is pair<iterator, bool>, and AFAIK iterator is a public typedef of the class.
BTW, I don't get why you do the second insert... to get to the inserted element? Probably you should declare element as a reference. In my suggested code:
Element &element = res.first->second;
Naturally, if you were using C++11, you could simply do:
auto res = ...;
Related
I am using a library that has:
typedef std::map<std::wstring, std::vector<std::wstring>* > XmlRecord_t;
When I iterate on this map, I create variables such as:
std::wstring& key( mapIter->first );
std::vector<std::wstring>* values( mapIter->second );
which is all fine. Then I want to iterate on the values, so I have a for loop such as:
for( std::vector<std::wstring>::const_iterator it = vals->cbegin();... )
which is fine and works well.
Now, as a curiosity only, I'd like to reuse the library's typedef names, in case the typedef definition changes, or for cleanliness. In other words, I'd like to use:
typedef XmlRecord_t::mapped_type VecPtr;
VecPtr values( mapIter->second )
instead of:
std::vector<std::wstring>* values( mapIter->second );
That works so far. The only issue comes from the for loop. My typedef is a pointer to vector, not just a vector. So I cannot use that typedef to replace:
std::vector<std::wstring>::const_iterator
in any way I know of.
Hence, my question generalizes to: if you have in a library (code I do not allow you to modify):
typedef map<KEY, T*> Map2Ptrs
how can you use Map2Ptrs in your code to get T (without the pointer)? To make a typedef of type T?
Obviously, the library authors should have created:
typedef std::vector<std::wstring> XmlRecordVals;
typedef std::map<std::wstring, XmlRecordVals* > XmlRecord_t;
instead. But I only have the original XmlRecord_t above to work with ;(
Assuming Map2Ptrs is always an std::map, its key and mapped type can be inspected with Map2Ptrs::key_type and Map2Ptrs::mapped_type respectively.
The pointer can be removed from a type with std::remove_pointer_t (requires C++14 and #include<type_traits>), e.g.:
using T = std::remove_pointer_t<Map2Ptrs::mapped_type>;
In modern C++ you probably should not specify the iterator types by hand, they can easily be deduced:
for( auto it = vals->cbegin();... )
Furthermore, there is also the range-for, which eliminates the need to specify the iterators manually and has much nicer syntax:
for(const auto& x : *vals) { /* Do something with x */ }
And since C++17, for the map structured bindings can be used:
for(const auto& [key, vals] : theMap) {
/* Do something with key and vals */
}
There is std::remove_pointer. Example from cppreference:
print_is_same<int, std::remove_pointer<int*>::type>(); // true
So if your aliased the pointer to vector type then you'd get the iterator via:
using Xml_const_iterator = std::remove_pointer<XmlRecord_t>type::const_iterator;
Though I would strongly suggest you to change the actual alias to remove the pointer instead.
I'm trying to make my code 1 line shorter, a noble cause. I have this unordered map
std::unordered_map<std::string, int> um;
and I want to assign the integer to a variable on the same line where I emplace a pair into the unordered map like so
int i_want_132_here = um.emplace("hi", 132).first.???;
problem is, I have no idea what to do with [return value of unordered_map::emplace].first
In the debugger I can see that "first" contains ("hi", 132) but how do I access those values?
emplace returns a pair<iterator, bool>.
So you should do:
int i_want_132_here = (*um.emplace("hi", 132).first).second;
alternative syntax:
int i_want_132_here = um.emplace("hi", 132).first->second;
In general I prefer (*it) form instead of it->.
Assuming a struct containing a std::vector with items of type std::pair.
struct block {
std::vector<std::pair<key_t, value_t>> items;
block(const Data& d) : items()
{
items = std::vector<std::pair<key_t, value_t>>(d.size_);
}
};
Later in the code, I assign a value to the vector at position 0:
block<key_t, value_t> b(*this);
std::pair<key_t, value_t> item = std::pair<key_t, value_t>(k, v);
b.items[0] = item;
Later in the code, I want to iterate over the vector and expect &bucket_item != NULL to be true only at position 0, because I only assigned a value at this position. In fact, &bucket_item != NULL is always true.
for (std::pair<key_t, value_t> item : b.items)
{
if (&item != NULL)
{
...
}
}
I am not able to initialize the vector with NULL values like so:
items = std::vector<std::pair<key_t, value_t>>(d.size_, NULL);
How to solve this?
It seems like you have Java background, C++ is a bit different.
items = std::vector<std::pair<key_t, value_t>>(d.size_);
items has already been initialized with its default constructor. The line above creates another default initialized container and assigns it to items, which is unnecessary.
In:
b.items[0] = item;
You need to make sure that the vector is big enough, because it does not allocate elements for you in operator[]. Either do b.items.push_front/back(item) or insert it at a specific position using vector::insert, e.g. b.items.insert(b.items.begin(), item). push_xxx and insert do allocate memory for new elements.
When you do
for (std::pair<key_t, value_t> item : b.items)
{
if (&item != NULL)
It iterates over all existing elements in the container. item is stored by value (unlike Java) in the container, so that it exists at a non-NULL address and cannot be possibly be equal to NULL.
However, expression for(std::pair<key_t, value_t> item : b.items) creates a copy of each element on the stack (also at non-NULL address), you probably want that to be for(std::pair<key_t, value_t>& item : b.items) to just refer to the element rather than copy it (note the ampersand symbol on the left of items). If you do not intend to modify item inside the loop, you may like to use const qualifier as well to make your intent clear to the reader of the code. e.g. for(std::pair<key_t, value_t> const& item : b.items).
And because you use C++11 anyway, you do not have to spell out the type of the container element, just use auto:
for (auto const& item : b.items)
// do something to item
When you create an std::vector with a length len like this std::vector<T> tVec(len), you are creating a vector with len default-constructed objects of type T. If you want to represent a null value, you will need to resort to one of the following ways:
Use a sentinel value of T to denote an invalid value.
Use a (smart-)pointer to T and use a nullptr as the natural invalid value.
Wrap a class around T which contains a bool marking it as invalid.
The last option is provided by boost::optional. Here's a rewrite of your code using it:
struct block {
using OptionalPair_t = boost::optional<std::pair<key_t, value_t>>;
std::vector<OptionalPair_t> items;
block(const Data& d) : items(d.size_)
{
}
};
Since boost::optional is contextually convertible to bool, you can do this:
for (auto& item : b.items)
{
if (item)
{
...
}
}
I think your mixing here a few definitions.
an element of std::vector cannot be NULL, because it's not a pointer. and it defenilty exists.
int arr[] = {1};, can arr[0] be null? of course not. why would it be ? it's a real integer who seat somwhere in the memory which is not null.
if you're iterating over a std::vector elements, that means they exist, so they can not be null.
NULL, or better nullptr, can be used to initialize a pointer value, but makes no sense to initialize, for example, an std::string or std::pair<std::string, int> with it.
If you want your vector to be empty, you can use:
std::vector<std::pair<key_t, value_t>> items;
othwerwise, if you want n default constructed std::pairs you can use:
std::vector<std::pair<key_t, value_t>> items(n);
Pair are really a comination of values. So you have to define which combination of values you consider as being the NULL equivalent for your pairs.
You can then initialize your vector with the following constructor:
std::vector<std::pair<key_t, value_t>> items(d.size_, make_pair(0,0)) ;
You just have to replace 0 with the neutral values for key_t and value_t.
Please note that vectors really contain values, and pairs are values. So there is no NULL pointers that would show absence of values.
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.
I'm using the STL map data structure, and at the moment my code first invokes find(): if the key was not previously in the map, it calls insert() it, otherwise it does nothing.
map<Foo*, string>::iterator it;
it = my_map.find(foo_obj); // 1st lookup
if(it == my_map.end()){
my_map[foo_obj] = "some value"; // 2nd lookup
}else{
// ok do nothing.
}
I was wondering if there is a better way than this, because as far as I can tell, in this case when I want to insert a key that is not present yet, I perform 2 lookups in the map data structures: one for find(), one in the insert() (which corresponds to the operator[] ).
Thanks in advance for any suggestion.
Normally if you do a find and maybe an insert, then you want to keep (and retrieve) the old value if it already existed. If you just want to overwrite any old value, map[foo_obj]="some value" will do that.
Here's how you get the old value, or insert a new one if it didn't exist, with one map lookup:
typedef std::map<Foo*,std::string> M;
typedef M::iterator I;
std::pair<I,bool> const& r=my_map.insert(M::value_type(foo_obj,"some value"));
if (r.second) {
// value was inserted; now my_map[foo_obj]="some value"
} else {
// value wasn't inserted because my_map[foo_obj] already existed.
// note: the old value is available through r.first->second
// and may not be "some value"
}
// in any case, r.first->second holds the current value of my_map[foo_obj]
This is a common enough idiom that you may want to use a helper function:
template <class M,class Key>
typename M::mapped_type &
get_else_update(M &m,Key const& k,typename M::mapped_type const& v) {
return m.insert(typename M::value_type(k,v)).first->second;
}
get_else_update(my_map,foo_obj,"some value");
If you have an expensive computation for v you want to skip if it already exists (e.g. memoization), you can generalize that too:
template <class M,class Key,class F>
typename M::mapped_type &
get_else_compute(M &m,Key const& k,F f) {
typedef typename M::mapped_type V;
std::pair<typename M::iterator,bool> r=m.insert(typename M::value_type(k,V()));
V &v=r.first->second;
if (r.second)
f(v);
return v;
}
where e.g.
struct F {
void operator()(std::string &val) const
{ val=std::string("some value")+" that is expensive to compute"; }
};
get_else_compute(my_map,foo_obj,F());
If the mapped type isn't default constructible, then make F provide a default value, or add another argument to get_else_compute.
There are two main approaches. The first is to use the insert function that takes a value type and which returns an iterator and a bool which indicate if an insertion took place and returns an iterator to either the existing element with the same key or the newly inserted element.
map<Foo*, string>::iterator it;
it = my_map.find(foo_obj); // 1st lookup
my_map.insert( map<Foo*, string>::value_type(foo_obj, "some_value") );
The advantage of this is that it is simple. The major disadvantage is that you always construct a new value for the second parameter whether or not an insertion is required. In the case of a string this probably doesn't matter. If your value is expensive to construct this may be more wasteful than necessary.
A way round this is to use the 'hint' version of insert.
std::pair< map<foo*, string>::iterator, map<foo*, string>::iterator >
range = my_map.equal_range(foo_obj);
if (range.first == range.second)
{
if (range.first != my_map.begin())
--range.first;
my_map.insert(range.first, map<Foo*, string>::value_type(foo_obj, "some_value") );
}
The insertiong is guaranteed to be in amortized constant time only if the element is inserted immediately after the supplied iterator, hence the --, if possible.
Edit
If this need to -- seems odd, then it is. There is an open defect (233) in the standard that hightlights this issue although the description of the issue as it applies to map is clearer in the duplicate issue 246.
In your example, you want to insert when it's not found. If default construction and setting the value after that is not expensive, I'd suggest simpler version with 1 lookup:
string& r = my_map[foo_obj]; // only lookup & insert if not existed
if (r == "") r = "some value"; // if default (obj wasn't in map), set value
// else existed already, do nothing
If your example tells what you actually want, consider adding that value as str Foo::s instead, you already have the object, so no lookups would be needed, just check if it has default value for that member. And keep the objs in the std::set. Even extending class FooWithValue2 may be cheaper than using map.
But If joining data through the map like this is really needed or if you want to update only if it existed, then Jonathan has the answer.