Related
I wanted to have something like
unordered_set<vector<pair<int,int>>> us;
but even without pair:
#include <vector>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<vector<int>> um;
}
it fails:
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::_Hash_code_base<std::vector<int>, std::vector<int>, std::__detail::_Identity, std::hash<std::vector<int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>’:
/usr/include/c++/4.8/bits/hashtable_policy.h:1402:10: required from ‘struct std::__detail::_Hashtable_base<std::vector<int>, std::vector<int>, std::__detail::_Identity, std::equal_to<std::vector<int> >, std::hash<std::vector<int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, true, true> >’
/usr/include/c++/4.8/bits/hashtable.h:174:11: required from ‘class std::_Hashtable<std::vector<int>, std::vector<int>, std::allocator<std::vector<int> >, std::__detail::_Identity, std::equal_to<std::vector<int> >, std::hash<std::vector<int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >’
/usr/include/c++/4.8/bits/unordered_set.h:96:18: required from ‘class std::unordered_set<std::vector<int> >’
prog.cpp:7:32: required from here
/usr/include/c++/4.8/bits/hashtable_policy.h:1070:12: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1070:12: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1082:53: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1082:53: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
prog.cpp: In constructor ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type = unsigned int; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::hasher = std::hash<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::vector<int> >]’:
prog.cpp:7:32: error: invalid use of incomplete type ‘std::unordered_set<std::vector<int> >::hasher {aka struct std::hash<std::vector<int> >}’
unordered_set<vector<int>> um;
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘std::unordered_set<std::vector<int> >::hasher {aka struct std::hash<std::vector<int> >}’
struct hash;
^
prog.cpp:7:32: note: when instantiating default argument for call to std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type = unsigned int; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::hasher = std::hash<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::vector<int> >]
unordered_set<vector<int>> um;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h: In instantiation of ‘std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, std::__detail::_Default_ranged_hash, true>::_Hash_code_base(const _ExtractKey&, const _H1&, const _H2&, const std::__detail::_Default_ranged_hash&) [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing]’:
/usr/include/c++/4.8/bits/hashtable_policy.h:1463:65: required from ‘std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::_Hashtable_base(const _ExtractKey&, const _H1&, const _H2&, const _Hash&, const _Equal&) [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
/usr/include/c++/4.8/bits/hashtable.h:828:24: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type, const _H1&, const _H2&, const _Hash&, const _Equal&, const _ExtractKey&, const allocator_type&) [with _Key = std::vector<int>; _Value = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::allocator_type = std::allocator<std::vector<int> >]’
/usr/include/c++/4.8/bits/hashtable.h:397:26: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type, const _H1&, const key_equal&, const allocator_type&) [with _Key = std::vector<int>; _Value = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::key_equal = std::equal_to<std::vector<int> >; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::allocator_type = std::allocator<std::vector<int> >]’
/usr/include/c++/4.8/bits/unordered_set.h:136:35: required from ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type = unsigned int; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::hasher = std::hash<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::vector<int> >]’
prog.cpp:7:32: required from here
/usr/include/c++/4.8/bits/hashtable_policy.h:1099:63: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
: __ebo_extract_key(__ex), __ebo_h1(__h1), __ebo_h2(__h2) { }
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1099:63: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
: __ebo_extract_key(__ex), __ebo_h1(__h1), __ebo_h2(__h2) { }
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
http://ideone.com/wusr5V
You could implement it like this, based on boost::hash_combine for a sensible calculation of hashes:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <unordered_set>
namespace
{
// a little helper that should IMHO be standardized
template<typename T>
std::size_t make_hash(const T& v)
{
return std::hash<T>()(v);
}
// adapted from boost::hash_combine
void hash_combine(std::size_t& h, const std::size_t& v)
{
h ^= v + 0x9e3779b9 + (h << 6) + (h >> 2);
}
// hash any container
template<typename T>
struct hash_container
{
size_t operator()(const T& v) const
{
size_t h=0;
for( const auto& e : v ) {
hash_combine(h, make_hash(e));
}
return h;
}
};
}
namespace std
{
// support for pair<T,U> if T and U can be hashed
template<typename T, typename U>
struct hash<pair<T, U>>
{
size_t operator()(const pair<T,U>& v) const
{
size_t h=make_hash(v.first);
hash_combine(h, make_hash(v.second));
return h;
}
};
// support for vector<T> if T is hashable
// (the T... is a required trick if the vector has a non-standard allocator)
template<typename... T>
struct hash<vector<T...>> : hash_container<vector<T...>> {};
// the same for map<T,U> if T and U are hashable
template<typename... T>
struct hash<map<T...>> : hash_container<map<T...>> {};
// simply add more containers as needed
}
int main()
{
std::unordered_set<std::vector<std::pair<int,int>>> us;
us.insert(std::vector<std::pair<int,int>>{{{42,0},{17,64}}});
std::cout << us.size() << std::endl;
std::cout << us.begin()->size() << std::endl;
std::cout << us.begin()->begin()->first << std::endl;
std::unordered_set<std::map<int,int>> usm;
std::map<int,int> m{{42,0},{17,64}};
usm.insert(m);
}
Live example
Note that there was a problem when combining Clang with libstdc++ which might affect you when specializing std::hash. It has been fixed with GCC 4.8.2, but it is still visible on Coliru. If this is a problem in your case, you could disable the warning with -Wno-mismatched-tags and Clang will no longer complain.
You should write a hasher for your types, for example:
class MyHash
{
public:
std::size_t operator()(const vector<pair<int,int>> &v) const
{
std::size_t x = 0;
for (auto &i : v)
x ^= std::hash<int>()(i.first) ^ std::hash<int>()(i.second);
return x;
}
};
int main()
{
unordered_set<vector<pair<int,int>>, MyHash> um;
}
Note: The hash function that I wrote is just an example, it can be replaced with another and better one.
I'm using C++17, MSYS2 MinGW64, OpenGL, stb_image on VScode on Windows. In my code below, I'm trying to recursively search a specific directory for every .png file to load textures (using the Texture object that takes in a std::string parameter). When I run my code, I get these errors:
C:/msys64/mingw64/include/c++/10.2.0/tuple: In instantiation of 'std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {int&&}; long long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long long unsigned int ..._Indexes2 = {}; _T1 = const int; _T2 = Texture]':
C:/msys64/mingw64/include/c++/10.2.0/tuple:1678:63: required from 'std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {int&&}; _Args2 = {}; _T1 = const int; _T2 = Texture]'
C:/msys64/mingw64/include/c++/10.2.0/ext/new_allocator.h:150:4: required from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const int, Texture>; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const int, Texture>, false>]'
C:/msys64/mingw64/include/c++/10.2.0/bits/alloc_traits.h:512:17: required from 'static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const int, Texture>; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const int, Texture>, false>; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<std::__detail::_Hash_node<std::pair<const int, Texture>, false> >]'
C:/msys64/mingw64/include/c++/10.2.0/bits/hashtable_policy.h:2037:36: required from 'std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args =
{const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const int, Texture>, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::allocator<std::__detail::_Hash_node<std::pair<const int, Texture>, false> >::value_type]'
C:/msys64/mingw64/include/c++/10.2.0/bits/hashtable.h:272:35: required from 'std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Scoped_node::_Scoped_node(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hashtable_alloc*, _Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Key = int; _Value = std::pair<const int, Texture>; _Alloc = std::allocator<std::pair<const int, Texture> >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<int>; _H1 = std::hash<int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__hashtable_alloc = std::_Hashtable<int, std::pair<const int, Texture>, std::allocator<std::pair<const int, Texture> >, std::__detail::_Select1st, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::__hashtable_alloc]'
C:/msys64/mingw64/include/c++/10.2.0/bits/hashtable_policy.h:739:42: required from 'std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = int; _Pair = std::pair<const int, Texture>; _Alloc = std::allocator<std::pair<const int, Texture> >; _Equal = std::equal_to<int>; _H1 = std::hash<int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = Texture; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = int]'
C:/msys64/mingw64/include/c++/10.2.0/bits/unordered_map.h:988:20: required from 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = int; _Tp = Texture; _Hash = std::hash<int>; _Pred = std::equal_to<int>; _Alloc = std::allocator<std::pair<const int, Texture> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = Texture; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = int]'
src/GameUI.cpp:32:50: required from here
C:/msys64/mingw64/include/c++/10.2.0/tuple:1689:70: error: no matching function for call to 'Texture::Texture()'
1689 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
How can I resolve this?
Also, are there any corrections or suggestions that could be made to improve my code (especially the map.emplace bit)?
Code:
#include "Texture.h"
#include <string>
#include <unordered_map>
#include <filesystem>
#include <iostream>
int main() {
std::unordered_map<int, Texture> map;
std::string path {"resources\\"};
std::string ext {".png"};
size_t count {};
for (auto &p : std::filesystem::recursive_directory_iterator(path)) {
if (p.path().extension() == ext) {
map.emplace(count, Texture{p.path().string()}); // Is this the most efficient way?
std::cout << p.path().string() << std::endl;
}
else if (!p.is_directory())
count++;
}
Texture test = map[7]; // Line 32 in GameUI.cpp
return 0;
}
Texture test = map[7];
map[7] returns a reference on a Texture, or default-constructs it if doesn't exist yet. If Texture doesn't have a default constructor, you get this error.
Try using map::at() instead, which throw an exception if map[7] doesn't exist. Or map::find() if you don't want exceptions. Or, well, just define Texture::Texture()...
map.emplace(count, Texture{p.path().string()}); // Is this the most efficient way?
I guess map.emplace(count, p.path().string()) should be enough, here?
Let the code speak it:
auto SetCompare = [](const string& a, const string& b)
{
size_t L = a.length();
size_t R = b.length();
if (L == R) return (a < b);
return L < R;
};
using MySet = std::set<string, decltype(SetCompare)>;
unordered_map<string, MySet> Map;
The insertion or access won't work:
Map["abc"];
Map["xyz"].insert("mapped to xyz");
// Insert TO set works
MySet mySet(SetCompare); // HINT
mySet.insert("x");
mySet.insert("abc");
// But not to the map!
Map.insert({"pqr", mySet});
At the HINTed location, I pass SetCompare lambda (not just type) to MySet's constructor. The question is how to pass it to the "Value" type of unordered_map?
EDIT (Compiler errors):
GCC C++17
In file included from /usr/include/c++/7/set:60:0,
from main.cpp:10:
/usr/include/c++/7/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree_key_compare<_Key_compare>::_Rb_tree_key_compare() [with _Key_compare = main()::<lambda(const string&, const string&)>]’:
/usr/include/c++/7/bits/stl_tree.h:688:4: required from ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {char&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/7/tuple:1641:63: required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {char&&}; _Args2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/7/ext/new_allocator.h:136:4: required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >]’
/usr/include/c++/7/bits/alloc_traits.h:475:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >]’
/usr/include/c++/7/bits/hashtable_policy.h:2066:37: required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false>]’
/usr/include/c++/7/bits/hashtable_policy.h:750:8: required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = char; _Pair = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; _Equal = std::equal_to<char>; _H1 = std::hash<char>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = char]’
/usr/include/c++/7/bits/unordered_map.h:977:20: required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = char; _Tp = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; _Hash = std::hash<char>; _Pred = std::equal_to<char>; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = char]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',31)">main.cpp:31:7</span>: required from here
/usr/include/c++/7/bits/stl_tree.h:149:24: error: use of deleted function ‘main()::::()’
: _M_key_compare()
^
main.cpp:17:24: note: a lambda closure type has a deleted default constructor
auto SetCompare = [](const string& a, const string& b)
GCC C++17
In file included from /usr/include/c++/6/set:60:0,
from main.cpp:10:
/usr/include/c++/6/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree_impl<_Key_compare, <anonymous> >::_Rb_tree_impl() [with _Key_compare = main()::<lambda(const string&, const string&)>; bool <anonymous> = false; _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = main()::<lambda(const string&, const string&)>; _Alloc = std::allocator<std::basic_string<char> >]’:
/usr/include/c++/6/bits/stl_tree.h:821:18: required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree() [with _Key = std::basic_string<char>; _Val = std::basic_string<char>; _KeyOfValue = std::_Identity<std::basic_string<char> >; _Compare = main()::<lambda(const string&, const string&)>; _Alloc = std::allocator<std::basic_string<char> >]’
/usr/include/c++/6/bits/stl_set.h:146:14: required from ‘std::set<_Key, _Compare, _Alloc>::set() [with _Key = std::basic_string<char>; _Compare = main()::<lambda(const string&, const string&)>; _Alloc = std::allocator<std::basic_string<char> >]’
/usr/include/c++/6/tuple:1590:70: required from ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {char&&}; long unsigned int ..._Indexes1 = {0ul}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/6/tuple:1579:63: required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {char&&}; _Args2 = {}; _T1 = const char; _T2 = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >]’
/usr/include/c++/6/ext/new_allocator.h:120:4: required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >]’
/usr/include/c++/6/bits/alloc_traits.h:475:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _Tp = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >]’
/usr/include/c++/6/bits/hashtable_policy.h:1953:37: required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<char&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >, false>]’
/usr/include/c++/6/bits/hashtable_policy.h:620:8: required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = char; _Pair = std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > >; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; _Equal = std::equal_to<char>; _H1 = std::hash<char>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = char]’
/usr/include/c++/6/bits/unordered_map.h:908:20: required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = char; _Tp = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; _Hash = std::hash<char>; _Pred = std::equal_to<char>; _Alloc = std::allocator<std::pair<const char, std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> > > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::set<std::basic_string<char>, main()::<lambda(const string&, const string&)> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = char]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',31)">main.cpp:31:7</span>: required from here
/usr/include/c++/6/bits/stl_tree.h:602:21: error: use of deleted function ‘main()::::()’
_M_node_count(0)
^
main.cpp:17:24: note: a lambda closure type has a deleted default constructor
auto SetCompare = [](const string& a, const string& b)
^
In file included from /usr/include/c++/6/set:60:0,
from main.cpp:10:
/usr/include/c++/6/bits/stl_tree.h:628:4: warning: ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree_impl<_Key_compare, >::_M_initialize() [with _Key_compare = main()::; bool = false; _Key = std::basic_string; _Val = std::basic_string; _KeyOfValue = std::_Identity >; _Compare = main()::; _Alloc = std::allocator >]’ used but never defined
_M_initialize()
^~~~~~~~~~~~~
VC C++14/17
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\set(84,1): error C2280: 'main::<lambda_b1395bcc88464fd844969fa0764f137c>::<lambda_b1395bcc88464fd844969fa0764f137c>(void)': attempting to reference a deleted function
1>xx.cpp(923): message : see declaration of 'main::<lambda_b1395bcc88464fd844969fa0764f137c>::<lambda_b1395bcc88464fd844969fa0764f137c>'
1>xxx.cpp(923,20): message : 'main::<lambda_b1395bcc88464fd844969fa0764f137c>::<lambda_b1395bcc88464fd844969fa0764f137c>(void)': function was explicitly deleted
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\set(84): message : while compiling class template member function 'std::set<std::string,main::<lambda_b1395bcc88464fd844969fa0764f137c>,std::allocator<std::string>>::set(void)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\tuple(975): message : see reference to function template instantiation 'std::set<std::string,main::<lambda_b1395bcc88464fd844969fa0764f137c>,std::allocator<std::string>>::set(void)' being compiled
1>xxx.cpp(937): message : see reference to class template instantiation 'std::set<std::string,main::<lambda_b1395bcc88464fd844969fa0764f137c>,std::allocator<std::string>>' being compiled
Lambdas are never default constructible before C++20 (see ClosureType::ClosureType() section on cppreference), so you must pass the SetCompare variable when constructing a MySet. Using -std=c++20/-std=c++2a on clang or GCC should work (demo), as well as /std:c++latest on MSVC.
To work around this limitation in earlier C++ versions define SetCompare as a functor type:
struct SetCompare
{
bool operator()(const string& a, const string& b)
{
size_t L = a.length();
size_t R = b.length();
if (L == R) return (a < b);
return L < R;
}
};
and use this type when defining MySet
using MySet = std::set<std::string, SetCompare>;
I stumbled upon a strange problem with an unordered_map.
First I generated an unordered_map<string, Person> and inserted a record ("Bob", Person(1, "Bob")) into the table. Then I tried to access the record by using the [] operator with key "Bob" and an error happened.
This is the code:
#include<iostream>
#include<unordered_map>
using namespace std;
class Person
{
public:
int play;
string name;
Person(int p, string n):play(p), name(n) {}
};
int main()
{
unordered_map<string,Person> test;
test.insert(std::make_pair("haha",Person(1,"haha")));
cout<<test["haha"].name<<endl;
return 0;
}
Errors occur when I compile the code with "g++ -S hash.cpp"
Output:
In file included from /usr/include/c++/7/unordered_map:41:0,
from hash.cpp:2:
/usr/include/c++/7/tuple: In instantiation of ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const std::__cxx11::basic_string<char>; _T2 = Person]’:
/usr/include/c++/7/tuple:1641:63: required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}; _Args2 = {}; _T1 = const std::__cxx11::basic_string<char>; _T2 = Person]’
/usr/include/c++/7/ext/new_allocator.h:136:4: required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const std::__cxx11::basic_string<char>, Person>; _Args = {const std::piecewise_construct_t&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Tp = std::pair<const std::__cxx11::basic_string<char>, Person>]’
/usr/include/c++/7/bits/alloc_traits.h:475:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const std::__cxx11::basic_string<char>, Person>; _Args = {const std::piecewise_construct_t&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Tp = std::pair<const std::__cxx11::basic_string<char>, Person>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const std::__cxx11::basic_string<char>, Person> >]’
/usr/include/c++/7/bits/hashtable_policy.h:2066:37: required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Person>, true> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Person>, true>]’
/usr/include/c++/7/bits/hashtable_policy.h:750:8: required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = std::__cxx11::basic_string<char>; _Pair = std::pair<const std::__cxx11::basic_string<char>, Person>; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, Person> >; _Equal = std::equal_to<std::__cxx11::basic_string<char> >; _H1 = std::hash<std::__cxx11::basic_string<char> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = Person; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = std::__cxx11::basic_string<char>]’
/usr/include/c++/7/bits/unordered_map.h:980:20: required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = std::__cxx11::basic_string<char>; _Tp = Person; _Hash = std::hash<std::__cxx11::basic_string<char> >; _Pred = std::equal_to<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, Person> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = Person; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::__cxx11::basic_string<char>]’
hash.cpp:17:19: required from here
/usr/include/c++/7/tuple:1652:70: error: no matching function for call to ‘Person::Person()’
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
^
hash.cpp:10:3: note: candidate: Person::Person(int, std::__cxx11::string)
Person(int p, string n):play(p), name(n) {}
^~~~~~
hash.cpp:10:3: note: candidate expects 2 arguments, 0 provided
hash.cpp:5:7: note: candidate: Person::Person(const Person&)
class Person
^~~~~~
hash.cpp:5:7: note: candidate expects 1 argument, 0 provided
hash.cpp:5:7: note: candidate: Person::Person(Person&&)
hash.cpp:5:7: note: candidate expects 1 argument, 0 provided
I had inserted the record but the map seemed to be unaware of this and tried to insert the record again.
So how does the g++ compiler process the [] operator?
Thanks.
Mapped type of unordered_map must be DefaultConstructible under operator[]. I.e. Person() must have a default constructor, if you want to be able to use operator[].
As an alternative, use at():
int main()
{
std::unordered_map<string,Person> test;
test.insert(std::make_pair("haha",Person(1,"haha")));
std::cout<<test.at("haha").name<<'\n';
return 0;
}
The mapped type has to be DefaultConstructible because operator[] should be able to create missing entries. The data in new entries is default constructed .
The problem is not with [] operator. Actual problem is that when the compiler executes the following line:
cout<<test["haha"].name<<endl;
It tries to create an object of type Person using it's default constructor. Since default constructor is not defined, it throws an error.
The following code will work fine. I have just added a default constructor.
#include<iostream>
#include<unordered_map>
using namespace std;
class Person
{
public:
int play;
string name;
Person(){} //Add default constructor
Person(int p, string n):play(p), name(n) {}
};
int main()
{
unordered_map<string,Person> test;
test.insert(std::make_pair("haha",Person(1,"haha")));
cout<<test["haha"].name<<endl;
return 0;
}
As explained in the comments and another answer, unordered_map<>::operator[] may need to create an object: if none is found for the key, it inserts a default constructed element and returns that.
If that is not what you want, you may simply attempt to find an element and only use it if present:
auto it = map.find("haha");
if(it != map.end()) {
cout << it->second.name;
}
Btw, internally the unordered_map holds as value_type a std::pair<key,Person>. Thus, if you hash by name (as you do), the Person's name is stored twice. One possible way to avoid this is the following construction:
struct personData
{
int play; // etc
};
using personMap = unordered_map<string,personData>;
using person = personMap::value_type;
The only inconvenience is that you have to access the data like play via person::second.play. One way to fix that is to define
struct person : private personMap::value_type
{
using base = personMap::value_type;
// must not add new data members to avoid slicing
personData& data() { return base.second; }
personData const& data() const { return base.second; }
int play() const { return data().play; }
string const&name() const { return base.first; }
};
auto fred = static_cast<person&>(map["fred"]);
The answers given are correct. I just want to add why the default constructor is not provided by the compiler in this case:
From online cpp reference
If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.
But if user-declared constructors of any kind are provided, the compiler will not generate a default constructor.
If some user-declared constructors are present, the user may still force the automatic generation of a default constructor by the compiler that would be implicitly-declared otherwise with the keyword default. (since C++11)
So you can add the following line to the code in the public section of the class and your code will work as intended:
Person() = default;
I wanted to have something like
unordered_set<vector<pair<int,int>>> us;
but even without pair:
#include <vector>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<vector<int>> um;
}
it fails:
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::_Hash_code_base<std::vector<int>, std::vector<int>, std::__detail::_Identity, std::hash<std::vector<int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, true>’:
/usr/include/c++/4.8/bits/hashtable_policy.h:1402:10: required from ‘struct std::__detail::_Hashtable_base<std::vector<int>, std::vector<int>, std::__detail::_Identity, std::equal_to<std::vector<int> >, std::hash<std::vector<int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, true, true> >’
/usr/include/c++/4.8/bits/hashtable.h:174:11: required from ‘class std::_Hashtable<std::vector<int>, std::vector<int>, std::allocator<std::vector<int> >, std::__detail::_Identity, std::equal_to<std::vector<int> >, std::hash<std::vector<int> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, true, true> >’
/usr/include/c++/4.8/bits/unordered_set.h:96:18: required from ‘class std::unordered_set<std::vector<int> >’
prog.cpp:7:32: required from here
/usr/include/c++/4.8/bits/hashtable_policy.h:1070:12: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1070:12: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1082:53: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1082:53: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
prog.cpp: In constructor ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type = unsigned int; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::hasher = std::hash<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::vector<int> >]’:
prog.cpp:7:32: error: invalid use of incomplete type ‘std::unordered_set<std::vector<int> >::hasher {aka struct std::hash<std::vector<int> >}’
unordered_set<vector<int>> um;
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘std::unordered_set<std::vector<int> >::hasher {aka struct std::hash<std::vector<int> >}’
struct hash;
^
prog.cpp:7:32: note: when instantiating default argument for call to std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type = unsigned int; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::hasher = std::hash<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::vector<int> >]
unordered_set<vector<int>> um;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h: In instantiation of ‘std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, std::__detail::_Default_ranged_hash, true>::_Hash_code_base(const _ExtractKey&, const _H1&, const _H2&, const std::__detail::_Default_ranged_hash&) [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing]’:
/usr/include/c++/4.8/bits/hashtable_policy.h:1463:65: required from ‘std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::_Hashtable_base(const _ExtractKey&, const _H1&, const _H2&, const _Hash&, const _Equal&) [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
/usr/include/c++/4.8/bits/hashtable.h:828:24: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type, const _H1&, const _H2&, const _Hash&, const _Equal&, const _ExtractKey&, const allocator_type&) [with _Key = std::vector<int>; _Value = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::allocator_type = std::allocator<std::vector<int> >]’
/usr/include/c++/4.8/bits/hashtable.h:397:26: required from ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable(std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type, const _H1&, const key_equal&, const allocator_type&) [with _Key = std::vector<int>; _Value = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::size_type = unsigned int; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::key_equal = std::equal_to<std::vector<int> >; std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::allocator_type = std::allocator<std::vector<int> >]’
/usr/include/c++/4.8/bits/unordered_set.h:136:35: required from ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type, const hasher&, const key_equal&, const allocator_type&) [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::size_type = unsigned int; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::hasher = std::hash<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::key_equal = std::equal_to<std::vector<int> >; std::unordered_set<_Value, _Hash, _Pred, _Alloc>::allocator_type = std::allocator<std::vector<int> >]’
prog.cpp:7:32: required from here
/usr/include/c++/4.8/bits/hashtable_policy.h:1099:63: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
: __ebo_extract_key(__ex), __ebo_h1(__h1), __ebo_h2(__h2) { }
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
In file included from /usr/include/c++/4.8/bits/hashtable.h:35:0,
from /usr/include/c++/4.8/unordered_set:47,
from prog.cpp:2:
/usr/include/c++/4.8/bits/hashtable_policy.h:1099:63: error: invalid use of incomplete type ‘struct std::hash<std::vector<int> >’
: __ebo_extract_key(__ex), __ebo_h1(__h1), __ebo_h2(__h2) { }
^
In file included from /usr/include/c++/4.8/bits/stl_bvector.h:1134:0,
from /usr/include/c++/4.8/vector:65,
from prog.cpp:1:
/usr/include/c++/4.8/bits/functional_hash.h:58:12: error: declaration of ‘struct std::hash<std::vector<int> >’
struct hash;
^
http://ideone.com/wusr5V
You could implement it like this, based on boost::hash_combine for a sensible calculation of hashes:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <unordered_set>
namespace
{
// a little helper that should IMHO be standardized
template<typename T>
std::size_t make_hash(const T& v)
{
return std::hash<T>()(v);
}
// adapted from boost::hash_combine
void hash_combine(std::size_t& h, const std::size_t& v)
{
h ^= v + 0x9e3779b9 + (h << 6) + (h >> 2);
}
// hash any container
template<typename T>
struct hash_container
{
size_t operator()(const T& v) const
{
size_t h=0;
for( const auto& e : v ) {
hash_combine(h, make_hash(e));
}
return h;
}
};
}
namespace std
{
// support for pair<T,U> if T and U can be hashed
template<typename T, typename U>
struct hash<pair<T, U>>
{
size_t operator()(const pair<T,U>& v) const
{
size_t h=make_hash(v.first);
hash_combine(h, make_hash(v.second));
return h;
}
};
// support for vector<T> if T is hashable
// (the T... is a required trick if the vector has a non-standard allocator)
template<typename... T>
struct hash<vector<T...>> : hash_container<vector<T...>> {};
// the same for map<T,U> if T and U are hashable
template<typename... T>
struct hash<map<T...>> : hash_container<map<T...>> {};
// simply add more containers as needed
}
int main()
{
std::unordered_set<std::vector<std::pair<int,int>>> us;
us.insert(std::vector<std::pair<int,int>>{{{42,0},{17,64}}});
std::cout << us.size() << std::endl;
std::cout << us.begin()->size() << std::endl;
std::cout << us.begin()->begin()->first << std::endl;
std::unordered_set<std::map<int,int>> usm;
std::map<int,int> m{{42,0},{17,64}};
usm.insert(m);
}
Live example
Note that there was a problem when combining Clang with libstdc++ which might affect you when specializing std::hash. It has been fixed with GCC 4.8.2, but it is still visible on Coliru. If this is a problem in your case, you could disable the warning with -Wno-mismatched-tags and Clang will no longer complain.
You should write a hasher for your types, for example:
class MyHash
{
public:
std::size_t operator()(const vector<pair<int,int>> &v) const
{
std::size_t x = 0;
for (auto &i : v)
x ^= std::hash<int>()(i.first) ^ std::hash<int>()(i.second);
return x;
}
};
int main()
{
unordered_set<vector<pair<int,int>>, MyHash> um;
}
Note: The hash function that I wrote is just an example, it can be replaced with another and better one.