set default value of unordered map if key doesn't exist - c++

In my program I want that the default value, if a key doesn't exist in my unordered map, is going to be std::numeric_limits<double>::max() Instead of just 0.
my code looks like the following:
class Pair
{
public:
Graph::NodeId node;
bitset<64> bits;
Pair(Graph::NodeId node1, double bits1)
{
node = node1;
bitset<64> temp(bits1);
bits = temp;
}
};
bool operator==(const Pair &P1, const Pair &P2)
{
return P1.node == P2.node && P1.bits == P2.bits;
}
template <>
struct std::hash<Pair>
{
std::size_t operator()(const Pair &pair) const noexcept
{
std::hash<decltype(pair.node)> ihash;
std::hash<decltype(pair.bits)> bhash;
return ihash(pair.node) * 31 + bhash(pair.bits);
}
};
unordered_map<Pair, double> lFunction;
so if I want to access the element lFunction[Pair(3,3)] and the key doesn't exist, there should be the value std::numeric_limits<double>::max().

One possibility would be to create a tiny class for the value type:
class dproxy {
double value_;
public:
dproxy(double value = std::numeric_limits<double>::max())
: value_{value} {}
operator double &() { return value_; }
operator double const &() const { return value_; }
};
std::unordered_map<Pair, dproxy> lFunction;
A default-constructed dproxy will be initialized to std::numeric_limits<double>::max(), and a dproxy will implicitly convert to a (reference to a) double. This can impose limitations if you're doing a lot of implicit conversions otherwise though (e.g., if you had some other function foo that took some other type bar that could be implicitly constructed from a double, foo(lFunction[baz]); would work if lFunction contained doubles, but not if it contained dproxy objects, since an implicit conversion sequence can only use one user-defined conversion).

if I want to access the element lFunction[Pair(3,3)] and the key doesn't exist, there should be the value std::numeric_limits<double>::max().
std::unordered_map::operator[] simply does not work that way. When it needs to insert a new value for a missing key, the new value is always value-initialized, which for numeric types means 0. There is no option to change that behavior in the unordered_map itself. But #JerryCoffin's answer shows you a workaround to take advantage of that behavior so you can define your own default value.
Otherwise, the alternative is to simply not use the map's operator[] at all. Use its find() and emplace() methods instead, eg:
auto getFunctionValue = [&](const Pair &key)
{
auto iter = lFunction.find(key);
if (iter == lFunction.end()) {
iter = lFunction.emplace(key, std::numeric_limits<double>::max()).first;
// or simply:
// return std::numeric_limits<double>::max();
}
return iter->second;
};
//double d = lFunction[Pair(3,3)];
double d = getFunctionValue(Pair(3,3));
...
Or, if you are using C++17 or later, you can use the try_emplace() method instead, eg:
auto getFunctionValue = [&](const Pair &key)
{
return lFunction.try_emplace(key, std::numeric_limits<double>::max()).first->second;
};
//double d = lFunction[Pair(3,3)];
double d = getFunctionValue(Pair(3,3));
...

Related

C++: Updating struct's values in a set during iteration in a for loop [duplicate]

I have a std::set<Foo>, and I'd like to update some value of
an existing element therein. Note that the value I'm updating does not change the order in the set:
#include <iostream>
#include <set>
#include <utility>
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
typedef std::set<Foo> Set;
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = p.second;
if (alreadyThere)
p.first->val += f.val; // error: assignment of data-member
// ‘Foo::val’ in read-only structure
}
int main(int argc, char** argv){
Set s;
update(s, Foo(1, 10));
update(s, Foo(1, 5));
// Now there should be one Foo object with val==15 in the set.
return 0;
}
Is there any concise way to do this? Or do I have to check if the element is already there, and if so, remove it, add the value and re-insert?
Since val is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
Don't try to solve this problem by working around the const-ness of items in a set. Instead, why not use map, which already expresses the key-value relationship you are modeling and provides easy ways to update existing elements.
Make val mutable as:
mutable int val;
Now you can change/modify/mutate val even if foo is const:
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
By the way, from your code, you seem to think that if p.second is true, then the value already existed in the set, and therefore you update the associated value. I think, you got it wrong. It is in fact other way round. The doc at cpluscplus says,
The pair::second element in the pair is set to true if a new element was inserted or false if an element with the same value existed.
which is correct, in my opinion.
However, if you use std::map, your solution would be straightforward:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
m[value.first] += value.second;
}
What does this code do? m[value.first] creates a new entry if the key doesn't exist in the map, and value of the new entry is default value of int which is zero. So it adds value.second to zero. Or else if the key exists, then it simply adds value.second to it. That is, the above code is equivalent to this:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
std::map<int,int>::iterator it = m.find(value);
if ( it != m.end()) //found or not?
it.second += value; //add if found
else
{
m.insert(value); //insert if not found
}
}
But this is too much, isn't it? It's performance is not good. The earlier one is more concise and very performant.
If you know what you're doing (the set elements are not const per se and you're not changing members involved in comparison), then you can just cast away const-ness:
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = !p.second;
if (alreadyThere)
{
Foo & item = const_cast<Foo&>(*p.first);
item.val += f.val;
}
}
you can use MAP witch has very fast access to your element if you have KEY . in this case i think using MAP would be better way to achieve fastest speed . STD::MAP

Insert Objects into Vector when Class Has Constants

Say I have a class that has only constants, since the values should never change.
struct Test {
const std::string id;
const int a;
const double b;
};
Later on, I want to add objects into a vector, but I want the vector to be sorted by b from largest to smallest. I use insertion sort, since there will only ever be a small number (maybe 5).
std::vector<Test> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && itr->b > b ) ++itr;
values.insert( itr, { id, a, b } );
// end sort
}
Attempting the above, I get the following error while attempting to insert into the vector:
error: object of type 'Test' cannot be assigned because its copy assignment operator is implicitly deleted
I would like to keep with using a vector for this problem; but I can't seem to find a way around the sorting issue. The only other option I could think of was to effectively recreate the vector constantly. Alternatively, using a multi-set or similar, then once all values are added, I could dump into an array.
Is there a way around this limitation, still using a vector and not making everything non-const? Or will I be forced to change my structure, or move away into a temporary object first?
Edit: Also trying to avoid the use of pointers
As has already been pointed out in the comments, sorting requires moving around elements in your vector. And moving around elements generally requires move-assignment, which generally requires mutation of elements…
There is one way out of this dillema: instead of assigning a new value to the existing object, create a new object with the new value on top of the existing one. One can do so by defining a copy/move constructor like, e.g.:
Test& operator =(Test&& other)
{
this->~Test();
return *new (this) Test(std::move(other));
}
There is just one problem with this: via [basic.life]/8.3, we're not allowed to use any existing pointer or reference to the original object, or even just the name of the original object after such an assignment. You would always have to use the result of the assignment (the return value of placement-new) or a laundered pointer as the sole means of accessing the object going forward. Since it is not specified how std::sort operates exactly, we cannoy rely on it doing so.
What we could to is build a wrapper like this (noexcept ommited for readability):
template <typename T>
class const_element
{
T value;
const T& laundered_value() const { return *std::launder(&value); }
T& laundered_value() { return *std::launder(&value); }
public:
template <typename... Args>
explicit const_element(Args&&... args) : value { std::forward<Args>(args)... } {}
const_element(const const_element& e) : value(e.laundered_value()) {}
const_element(const_element&& e) : value(std::move(e.laundered_value())) {}
const_element& operator =(const const_element& e)
{
laundered_value().~T();
new (&value) T(e.laundered_value());
return *this;
}
const_element& operator =(const_element&& e)
{
laundered_value().~T();
new (&value) T(std::move(e.laundered_value()));
return *this;
}
~const_element()
{
laundered_value().~T();
}
operator const T&() const { return laundered_value(); }
operator T&() { return laundered_value(); }
friend bool operator <(const_element& a, const_element& b)
{
return a.laundered_value() < b.laundered_value();
}
};
What this does is wrap an object of some type T, implement copy and move assignment in the way described above, and make sure that any access to the current value always goes through a laundered pointer.
Then we can just do
std::vector<const_element<Test>> values;
values.emplace_back("b", 1, 0.0);
values.emplace_back("a", 0, 0.0);
values.emplace_back("c", 2, 0.0);
std::sort(begin(values), end(values));
working example here
All that being said, I would recommend to just not do this. If you want an object that cannot be modified, simply use a const T rather than a T that solely consists of const members. You cannot have a vector of const T. But you can have a vector of T and then just pass around a reference to a const vector or a range of const elements…
You could store pointers in the vector. Of course, you would also need to clean everything up. Example at http://cpp.sh/3h7dr
std::vector<Test*> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && ((*itr)->b > b) ) ++itr;
Test* t = new Test{id,a,b};
values.insert( itr, t );
// end sort
}

Change custom class value in range based for loop [duplicate]

I have a std::set<Foo>, and I'd like to update some value of
an existing element therein. Note that the value I'm updating does not change the order in the set:
#include <iostream>
#include <set>
#include <utility>
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
typedef std::set<Foo> Set;
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = p.second;
if (alreadyThere)
p.first->val += f.val; // error: assignment of data-member
// ‘Foo::val’ in read-only structure
}
int main(int argc, char** argv){
Set s;
update(s, Foo(1, 10));
update(s, Foo(1, 5));
// Now there should be one Foo object with val==15 in the set.
return 0;
}
Is there any concise way to do this? Or do I have to check if the element is already there, and if so, remove it, add the value and re-insert?
Since val is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
Don't try to solve this problem by working around the const-ness of items in a set. Instead, why not use map, which already expresses the key-value relationship you are modeling and provides easy ways to update existing elements.
Make val mutable as:
mutable int val;
Now you can change/modify/mutate val even if foo is const:
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
By the way, from your code, you seem to think that if p.second is true, then the value already existed in the set, and therefore you update the associated value. I think, you got it wrong. It is in fact other way round. The doc at cpluscplus says,
The pair::second element in the pair is set to true if a new element was inserted or false if an element with the same value existed.
which is correct, in my opinion.
However, if you use std::map, your solution would be straightforward:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
m[value.first] += value.second;
}
What does this code do? m[value.first] creates a new entry if the key doesn't exist in the map, and value of the new entry is default value of int which is zero. So it adds value.second to zero. Or else if the key exists, then it simply adds value.second to it. That is, the above code is equivalent to this:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
std::map<int,int>::iterator it = m.find(value);
if ( it != m.end()) //found or not?
it.second += value; //add if found
else
{
m.insert(value); //insert if not found
}
}
But this is too much, isn't it? It's performance is not good. The earlier one is more concise and very performant.
If you know what you're doing (the set elements are not const per se and you're not changing members involved in comparison), then you can just cast away const-ness:
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = !p.second;
if (alreadyThere)
{
Foo & item = const_cast<Foo&>(*p.first);
item.val += f.val;
}
}
you can use MAP witch has very fast access to your element if you have KEY . in this case i think using MAP would be better way to achieve fastest speed . STD::MAP

Change the const of an element of a struct

I have a pointer to a:
struct KeyValue {
K key;
V value;
}
and I want to return a reference to the same memory location interpreted as a
struct IKeyValue {
const K key;
V value;
}
Is there a clean way to do it in C++11?
The reference to IKeyValue should be valid as long as any reference to the original KeyValue is valid.
The reason I am doing this is because I am implementing a HashTable<K, V> using open addressing. As a consequence I have an array of KeyValue with some of them being fully constructed (key and value constructed) and some of them half constructed (only key constructed to empty_key or tombstone_key). I want the iterators to this structure be used such that it->key can be read but not modified and it->value can be read and modified. And I prefer not to have something such as it->key() and it->value() as it is done in the standard library.
As it seems difficult to do this, I am thinking of another option as suggested by the answers. I can construct an array of IKeyValue inside my HashTable with:
IKeyValue* p = (IKeyValue*)malloc(n * sizeof(IKeyValue));
But then, am I allowed to use emplacement new in order to construct just a key or a value with placement new?
You can provide reference members and a constructor
struct IKeyValue {
const K& key;
V& value;
IKeyValue(KeyValue& x) : key(x.key), value(x.value) {}
};
But as mentioned in the comment, the lifetime management for the referenced instances could become a problem.
In IKeyValue, use accessor functions rather than data fields:
struct IKeyValue {
private:
KeyValue & keyValue;
public:
IKeyValue (KeyValue & keyValue) : keyValue (keyValue) {}
K const & key () const { return keyValue.key; }
V & value () { return keyValue.value; }
V const & value () const { return keyValue.value; }
};
KeyValue kv = ...;
IKeyValue ikv (kv);
... ikv.key (); // returns kv.key;
ikv.value () = ...; // sets kv.value;
You can't , as they are not the same type, nor IKeyValue is a derived class of KeyValue.
You need to redesign your code to use something different, like returning a view of the class instead.
The cleanest way to do this is to make your data members private, and provide getters and setters when needed. From what you wrote, it seems that you want the key to be read-only, and the value to be read-write. So you can do this:
class KeyValue
{
public:
KeyValue(K k, V v) : key(k), value(v) {}
K get_key() const {return key;}
V get_value() const {return value;}
void set_value(V v) {value = v;}
private:
K key;
V value;
};
Now the key can only be set in the constructor, and is read-only from that moment on.
Okay, this is quite a weird solution I'm offering here, and some would argue an abusive one, but if your "Value" is always modifiable, you could do something like:
struct KeyValue {
K key;
mutable V value;
};
and then hold a const KeyValue& where you want to lock in the constness of the key.

How to update an existing element of std::set?

I have a std::set<Foo>, and I'd like to update some value of
an existing element therein. Note that the value I'm updating does not change the order in the set:
#include <iostream>
#include <set>
#include <utility>
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
typedef std::set<Foo> Set;
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = p.second;
if (alreadyThere)
p.first->val += f.val; // error: assignment of data-member
// ‘Foo::val’ in read-only structure
}
int main(int argc, char** argv){
Set s;
update(s, Foo(1, 10));
update(s, Foo(1, 5));
// Now there should be one Foo object with val==15 in the set.
return 0;
}
Is there any concise way to do this? Or do I have to check if the element is already there, and if so, remove it, add the value and re-insert?
Since val is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
Don't try to solve this problem by working around the const-ness of items in a set. Instead, why not use map, which already expresses the key-value relationship you are modeling and provides easy ways to update existing elements.
Make val mutable as:
mutable int val;
Now you can change/modify/mutate val even if foo is const:
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
By the way, from your code, you seem to think that if p.second is true, then the value already existed in the set, and therefore you update the associated value. I think, you got it wrong. It is in fact other way round. The doc at cpluscplus says,
The pair::second element in the pair is set to true if a new element was inserted or false if an element with the same value existed.
which is correct, in my opinion.
However, if you use std::map, your solution would be straightforward:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
m[value.first] += value.second;
}
What does this code do? m[value.first] creates a new entry if the key doesn't exist in the map, and value of the new entry is default value of int which is zero. So it adds value.second to zero. Or else if the key exists, then it simply adds value.second to it. That is, the above code is equivalent to this:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
std::map<int,int>::iterator it = m.find(value);
if ( it != m.end()) //found or not?
it.second += value; //add if found
else
{
m.insert(value); //insert if not found
}
}
But this is too much, isn't it? It's performance is not good. The earlier one is more concise and very performant.
If you know what you're doing (the set elements are not const per se and you're not changing members involved in comparison), then you can just cast away const-ness:
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = !p.second;
if (alreadyThere)
{
Foo & item = const_cast<Foo&>(*p.first);
item.val += f.val;
}
}
Instead of separate hint, use the erase's return value for the next iterator position
bool alreadyThere = !p.second;
if (alreadyThere)
{
auto nit = s.erase(p.first);
s.insert(nit, f);
}
you can use MAP witch has very fast access to your element if you have KEY . in this case i think using MAP would be better way to achieve fastest speed . STD::MAP