How to use lambda function as hash function in unordered_map? - c++

I wonder if it is possible to use lambda function as custom hash function for unordered_map in C++11? If so, what is the syntax?

#include<unordered_map>
#include<string>
int main() {
auto my_hash = [](std::string const& foo) {
return std::hash<std::string>()(foo);
};
std::unordered_map<std::string, int, decltype(my_hash)> my_map(10, my_hash);
}
You need to pass lambda object to unordered_map constructor, since lambda types are not default constructible.
As #mmocny suggested in comment, it's also possible to define make function to enable type deduction if you really want to get rid of decltype:
#include<unordered_map>
#include<string>
template<
class Key,
class T,
class Hash = std::hash<Key>
// skipped EqualTo and Allocator for simplicity
>
std::unordered_map<Key, T, Hash> make_unordered_map(
typename std::unordered_map<Key, T, Hash>::size_type bucket_count = 10,
const Hash& hash = Hash()) {
return std::unordered_map<Key, T, Hash>(bucket_count, hash);
}
int main() {
auto my_map = make_unordered_map<std::string, int>(10,
[](std::string const& foo) {
return std::hash<std::string>()(foo);
});
}

Related

using a map with a comparator as a std::map parameter

Say I define a map with a custom comparator such as
struct Obj
{
int id;
std::string data;
std::vector<std::string> moreData;
};
struct Comparator
{
using is_transparent = std::true_type;
bool operator()(Obj const& obj1, Obj const& obj2) { return obj1.id < obj2.id; };
}
std::map<Obj,int,Comparator> compMap;
is there a good way to ensure that downstream users don't have to implement the comparator to use the map as a map?
for instance my compiler throws an error if I try to pass it to a function with a similar type.
template<class T>
inline void add(std::map<T, int>& theMap, T const & keyObj)
{
auto IT = theMap.find(keyObj);
if (IT != theMap.end())
IT->second++;
else
theMap[keyObj] = 1;
}
add(compMap,newObj); //type error here
EDIT:
I kinda over santitized this to make a generic case. and then overlooked the obvious
template<class T, class Comp, class Alloc>
inline void add(std::map<T, int, Comp, Alloc>& theMap, T const & keyObj)
still having issues with one use not being able to deduce T, but went from 80 erros to 1 so... progress
thanks everyone.
You can typedef the specialised type and use that type inplace of
std::map<...
typedef std::map<Obj,int,Comparator> compMap_t;
inline void add(compMap_t& theMap, Obj const & keyObj)
...
Downstream users either use the type declared by you
using my_important_map = std::map<Obj,int,Comparator>;
or better use functions which take a generic map type,
auto some_function(auto const& map_)
{
//do something with the map and don't care about the ordering
return map_.find(Obj(1));
}

Sorting order of std::map depending on an input value

I know that you cannot change the ordering of a map once declared. Instead I am trying this in a struct:
struct MyStruct
{
std::map<int, double>* my_map;
MyStruct(bool dir)
{
if(dir)
{
my_map = new std::map<int, double, std::less<int> >;
}
else
{
my_map = new std::map<int, double, std::greater<int> >;
}
}
}
This isn't working and complains that I am changing the type under the else condition. Is there a way around this? The only way I can think of is to write my own comparator and to create an object encapsulating bool dir which seems redundant.
std::map takes the comparison object as a template parameter, so to do what you want you need a type that you can change the behaviour at runtime.
struct MoreOrLess
{
bool useLess;
template <class T, class U>
bool operator()(const T &t, const U &u) const
{
if(useLess) return t < u;
else return t > u;
}
};
struct MyStruct
{
std::map<int, double, MoreOrLess> my_map;
MyStruct(bool dir) :my_map(MoreOrLess{dir}) {}
};
This way the comparison functor has the same type (for use in std::map) regardless of using std::less or std::greater.
Here is a simple approach, although probably not the most efficient:
struct MyStruct
{
typedef std::function<bool(int,int)> Predicate;
std::map<int,double,Predicate> my_map;
static Predicate predicateFor(bool dir)
{
if (dir) return std::less<int>();
return std::greater<int>();
}
MyStruct(bool dir) : my_map(predicateFor(dir)) { }
};

How do i write a hash function in C++?

How do i declare a hash function for my custom type so that i could use it in an unordered_map?
namespace std {
template<>
struct hash<my_custom_type>
{
using argument_type = my_custom_type;
using result_type = size_t;
size_t operator()(my_custom_type const& x) const
{
// Perform your hash algorithm here.
}
};
}
Probably the best way is to provide a specialization of std::hash< class Key > with std::hash< mytype >.

override map::compare with lambda function directly

Trying to override map::compare function using lambda, it seems that the following solution works.
auto cmp = [](const int&a, const int& b) { return a < b; };
std::map<int, int, decltype(cmp)> myMap(cmp);
But, I had to define cmp first and use it later.
Can I do this without defining 'cmp'?
No, you can't use lambda in unevaluated context -- i.e. template parameters as in your example.
So you must define it somewhere else (using auto) and then use decltype... the other way, as it was mentioned already is to use an "ordinal" functors
If your question is about "how to use lambda expression *once* when define a map" you can exploit implicit conversion of lambdas to std::function like this:
#include <iostream>
#include <functional>
#include <map>
int main()
{
auto m = std::map<int, int, std::function<bool(const int&, const int&)>>{
[](const int& a, const int& b)
{
return a < b;
}
};
return 0;
}
you may introduce an alias for that map type to reduce typing later...
#include <iostream>
#include <functional>
#include <map>
#include <typeinfo>
typedef std::map< int, int, std::function<bool(const int&, const int&)> > MyMap;
int main()
{
auto cmp = [](const int& a, const int& b) { return a < b; };
MyMap map(cmp);
return 0;
}
Using std::function to provide the appropriate type signature for the comparator type you can define your map type and then assign any lambda compare you wish to.
You could do something like this where the type of the map is deduced from the function you pass to a function.
#include <map>
template<class Key, class Value, class F>
std::map<Key, Value, F> make_map(const F& f) {
return std::map<Key, Value, F>{f};
}
int main() {
auto my_map = make_map<int, int>([](const int&a, const int& b) { return a < b; });
my_map[10] = 20;
}
I don't see a ton of reason for doing this but I wont say it's useless. Generally you want a known comparator so that the map can be passed around easily. With the setup above you are reduced to using template functions all the time like the following
tempalte<class F>
void do_somthing(const std::map<int, int, F>& m) {
}
This isn't necessarily bad but my instincts tell me that having a type which can ONLY be dealt with by generic functions is bad. I think it works out fine for lambda functions but that's about it. The solution here is to use std::function
#include <map>
#include <functional>
template<class Key, class Value>
using my_map_t = std::map<Key, Value, std::function<bool(const Key&, const Key&)>>;
int main() {
my_map_t<int, int> my_map{[](const int&a, const int& b) { return a < b; }};
my_map[10] = 20;
}
Now you can use any predicate you want and you have a concrete type to work with, my_map
hope this helps!
In C++20 you can do this:
std::map<int, int, decltype([](const int&a, const int& b) { return a < b; })> myMap;
int main() {
myMap.insert({7, 1});
myMap.insert({46, 2});
myMap.insert({56, 3});
for (const auto& [key,value]:myMap) {
std::cout << key << " " << value << std::endl;
}
}

std::map default value

Is there a way to specify the default value std::map's operator[] returns when an key does not exist?
While this does not exactly answer the question, I have circumvented the problem with code like this:
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
No, there isn't. The simplest solution is to write your own free template function to do this. Something like:
#include <string>
#include <map>
using namespace std;
template <typename K, typename V>
V GetWithDef(const std::map <K,V> & m, const K & key, const V & defval ) {
typename std::map<K,V>::const_iterator it = m.find( key );
if ( it == m.end() ) {
return defval;
}
else {
return it->second;
}
}
int main() {
map <string,int> x;
...
int i = GetWithDef( x, string("foo"), 42 );
}
C++11 Update
Purpose: Account for generic associative containers, as well as optional comparator and allocator parameters.
template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
typename C<K,V,Args...>::const_iterator it = m.find( key );
if (it == m.end())
return defval;
return it->second;
}
C++17 provides try_emplace which does exactly this. It takes a key and an argument list for the value constructor and returns a pair: an iterator and a bool.: http://en.cppreference.com/w/cpp/container/map/try_emplace
The C++ standard (23.3.1.2) specifies that the newly inserted value is default constructed, so map itself doesn't provide a way of doing it. Your choices are:
Give the value type a default constructor that initialises it to the value you want, or
Wrap the map in your own class that provides a default value and implements operator[] to insert that default.
The value is initialized using the default constructor, as the other answers say. However, it is useful to add that in case of simple types (integral types such as int, float, pointer or POD (plan old data) types), the values are zero-initialized (or zeroed by value-initialization (which is effectively the same thing), depending on which version of C++ is used).
Anyway, the bottomline is, that maps with simple types will zero-initialize the new items automatically. So in some cases, there is no need to worry about explicitly specifying the default initial value.
std::map<int, char*> map;
typedef char *P;
char *p = map[123],
*p1 = P(); // map uses the same construct inside, causes zero-initialization
assert(!p && !p1); // both will be 0
See Do the parentheses after the type name make a difference with new? for more details on the matter.
There is no way to specify the default value - it is always value constructed by the default (zero parameter constructor).
In fact operator[] probably does more than you expect as if a value does not exist for the given key in the map it will insert a new one with the value from the default constructor.
template<typename T, T X>
struct Default {
Default () : val(T(X)) {}
Default (T const & val) : val(val) {}
operator T & () { return val; }
operator T const & () const { return val; }
T val;
};
<...>
std::map<KeyType, Default<ValueType, DefaultValue> > mapping;
More General Version, Support C++98/03 and More Containers
Works with generic associative containers, the only template parameter is the container type itself.
Supported containers: std::map, std::multimap, std::unordered_map, std::unordered_multimap, wxHashMap, QMap, QMultiMap, QHash, QMultiHash, etc.
template<typename MAP>
const typename MAP::mapped_type& get_with_default(const MAP& m,
const typename MAP::key_type& key,
const typename MAP::mapped_type& defval)
{
typename MAP::const_iterator it = m.find(key);
if (it == m.end())
return defval;
return it->second;
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
Here is a similar implementation by using a wrapper class, which is more similar to the method get() of dict type in Python: https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hpp
template<typename MAP>
struct map_wrapper
{
typedef typename MAP::key_type K;
typedef typename MAP::mapped_type V;
typedef typename MAP::const_iterator CIT;
map_wrapper(const MAP& m) :m_map(m) {}
const V& get(const K& key, const V& default_val) const
{
CIT it = m_map.find(key);
if (it == m_map.end())
return default_val;
return it->second;
}
private:
const MAP& m_map;
};
template<typename MAP>
map_wrapper<MAP> wrap_map(const MAP& m)
{
return map_wrapper<MAP>(m);
}
Usage:
std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
One workaround is to use map::at() instead of [].
If a key does not exist, at throws an exception.
Even nicer, this also works for vectors, and is thus suited for generic programming where you may swap the map with a vector.
Using a custom value for unregistered key may be dangerous since that custom value (like -1) may be processed further down in the code. With exceptions, it's easier to spot bugs.
Expanding on the answer https://stackoverflow.com/a/2333816/272642, this template function uses std::map's key_type and mapped_type typedefs to deduce the type of key and def.
This doesn't work with containers without these typedefs.
template <typename C>
typename C::mapped_type getWithDefault(const C& m, const typename C::key_type& key, const typename C::mapped_type& def) {
typename C::const_iterator it = m.find(key);
if (it == m.end())
return def;
return it->second;
}
This allows you to use
std::map<std::string, int*> m;
int* v = getWithDefault(m, "a", NULL);
without needing to cast the arguments like std::string("a"), (int*) NULL.
Pre-C++17, use std::map::insert(), for newer versions use try_emplace(). It may be counter-intuitive, but these functions effectively have the behaviour of operator[] with custom default values.
Realizing that I'm quite late to this party, but if you're interested in the behaviour of operator[] with custom defaults (that is: find the element with the given key, if it isn't present insert a chosen default value and return a reference to either the newly inserted value or the existing value), there is already a function available to you pre C++17: std::map::insert(). insert will not actually insert if the key already exists, but instead return an iterator to the existing value.
Say, you wanted a map of string-to-int and insert a default value of 42 if the key wasn't present yet:
std::map<std::string, int> answers;
int count_answers( const std::string &question)
{
auto &value = answers.insert( {question, 42}).first->second;
return value++;
}
int main() {
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
return 0;
}
which should output 42, 43 and 44.
If the cost of constructing the map value is high (if either copying/moving the key or the value type is expensive), this comes at a significant performance penalty, which would be circumvented with C++17's try_emplace().
If you have access to C++17, my solution is as follows:
std::map<std::string, std::optional<int>> myNullables;
std::cout << myNullables["empty-key"].value_or(-1) << std::endl;
This allows you to specify a 'default value' at each use of the map. This may not necessarily be what you want or need, but I'll post it here for the sake of completeness. This solution lends itself well to a functional paradigm, as maps (and dictionaries) are often used with such a style anyway:
Map<String, int> myNullables;
print(myNullables["empty-key"] ?? -1);
Maybe you can give a custom allocator who allocate with a default value you want.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
With C++20 it is simple to write such getter:
constexpr auto &getOrDefault(const auto &map, const auto &key, const auto &defaultValue)
{
const auto itr = map.find(key);
return itr == map.cend() ? defaultValue : itr->second;
}
Here is a correct approach that will conditionally return a reference if the caller passes in an lvalue reference to the mapped type.
template <typename Map, typename DefVal>
using get_default_return_t = std::conditional_t<std::is_same_v<std::decay_t<DefVal>,
typename Map::mapped_type> && std::is_lvalue_reference_v<DefVal>,
const typename Map::mapped_type&, typename Map::mapped_type>;
template <typename Map, typename Key, typename DefVal>
get_default_return_t<Map, DefVal> get_default(const Map& map, const Key& key, DefVal&& defval)
{
auto i = map.find(key);
return i != map.end() ? i->second : defval;
}
int main()
{
std::map<std::string, std::string> map;
const char cstr[] = "world";
std::string str = "world";
auto& ref = get_default(map, "hello", str);
auto& ref2 = get_default(map, "hello", std::string{"world"}); // fails to compile
auto& ref3 = get_default(map, "hello", cstr); // fails to compile
return 0;
}
If you would like to keep using operator[] just like when you don't have to specify a default value other than what comes out from T() (where T is the value type), you can inherit T and specify a different default value in the constructor:
#include <iostream>
#include <map>
#include <string>
int main() {
class string_with_my_default : public std::string {
public:
string_with_my_default() : std::string("my default") {}
};
std::map<std::string, string_with_my_default> m;
std::cout << m["first-key"] << std::endl;
}
However, if T is a primitive type, try this:
#include <iostream>
#include <map>
#include <string>
template <int default_val>
class int_with_my_default {
private:
int val = default_val;
public:
operator int &() { return val; }
int* operator &() { return &val; }
};
int main() {
std::map<std::string, int_with_my_default<1> > m;
std::cout << m["first-key"] << std::endl;
++ m["second-key"];
std::cout << m["second-key"] << std::endl;
}
See also C++ Class wrapper around fundamental types