samedi 24 janvier 2015

Overloading function to handle multiple value classes in C++11?

I'm writing a C++11 hashmap class (mostly for fun), and I'd like my insert function to be able to handle any value class efficiently (ie it uses move semantics when possible).


So what I did is write 4 overloads of my insert:



template <class K, class V>
struct hashmap {
<snip>
hashmap<K,V>& insert (K&& key, V&& val);
hashmap<K,V>& insert (K key, V&& val);
hashmap<K,V>& insert (K&& key, V val);
hashmap<K,V>& insert (K key, V val);
<snip>
};


And defined them so the one with two rvalue references is the "real" insert, and the others use std::forward/std::move as appropriate to pass their arguments to it.



// insert operator. If the key doesn't exist in the table, build it with
// the given value. If it does exist, overwrite it with new value. Other
// insert variants wrap this with appropriate forward/move semantics
template <class K, class V>
hashmap<K,V>& hashmap<K,V>::insert(K&& key, V&& val) {
using std::swap; // for ADL
using std::move;
using std::forward;

if (capacity_ == 0) {
resize_(DEFAULT_SIZE);
}

size_t hash = hash_key(key);
size_t idx = modulo(hash, capacity_);

// scan through container until we find an empty bucket. If we find the key
// along the way, just set its value and we're done. If we find any elements
// with a smaller probe distance than ours, swap with them and continue loop
bool swapped=false;
for (size_t ii=0, curdist=0; ii < capacity_; ii++, curdist++) {
size_t pos = modulo(idx + ii, capacity_);

if (storage_[pos].hash == EMPTY_BUCKET) {
// found an empty bucket, place what we currently have there
if (used_ >= 3*capacity_/4) {
// if loading is > 75% then resize the table and call back to insert to place element
resize_(2*capacity_);
insert(forward<K>(key), forward<V>(val));
} else {
// otherwise just build the new hash bucket as requested
storage_[pos].hash = hash;
new (&storage_[pos].kv.key) K(forward<K>(key));
new (&storage_[pos].kv.val) V(forward<V>(val));
used_++;
}
break;

} else if (!swapped && storage_[pos].hash == hash && (storage_[pos].kv.key == key)) {
// found a bucket that matches our key, update it
storage_[pos].kv.val = forward<V>(val);
break;

} else {
// found non-empty bucket that doesn't match us. If it has a probe distance
// less than ours, we'll swap with it and continue looking to place the element
size_t newdist = probedist(storage_[pos].hash, pos);
if (newdist < curdist) {
curdist = newdist;
swap(storage_[pos].hash, hash);
swap(storage_[pos].kv.key, key);
swap(storage_[pos].kv.val, val);
swapped = true;
}
}
}

return *this;
}


// insert variants for different value classes
template <class K, class V>
hashmap<K,V>& hashmap<K,V>::insert(K key, V&& val) { insert(std::move(key), std::forward(val)); return *this; }

template <class K, class V>
hashmap<K,V>& hashmap<K,V>::insert(K&& key, V val) { insert(std::forward(key), std::move(val)); return *this; }

template <class K, class V>
hashmap<K,V>& hashmap<K,V>::insert(K key, V val) { insert(std::move(key), std::move(val)); return *this; }


Naturally, after I made this change, I tried to insert a std::string, size_t pair into an instance:



maph.insert(*ptr++, ii);


And immediately got a compiler error complaining about ambiguous calls:



src/check_hash.cc:313:5: required from here
src/check_hash.cc:128:13: error: call of overloaded ‘insert(std::basic_string<char>&, size_t&)’ is ambiguous
maph.insert(*ptr++, ii);
^
src/check_hash.cc:128:13: note: candidates are:
In file included from src/check_hash.cc:9:
hashmap.h:243:23: note: prelude::{anonymous}::hashmap<K, V>& prelude::{anonymous}::hashmap<K, V>::insert(K, V&&) [with K = std::basic_string<char>; V = int]
hashmap<K,V>& hashmap<K,V>::insert(K key, V&& val) { insert(std::move(key), std::forward(val)); return *this; }
^
prelude/hashmap.h:39:27: note: prelude::{anonymous}::hashmap<K, V>& prelude::{anonymous}::hashmap<K, V>::insert(K, V) [with K = std::basic_string<char>; V = int]
hashmap<K,V>& insert (K key, V val);




What would be the right way to do this to be able to accomodate all 4 combinations of key/value types?


Aucun commentaire:

Enregistrer un commentaire