I am new to C++ and STL. I am stuck with the following simple example of a hash set storing custom data structures:
#include <iostream>
#include <ext/hash_set>
using namespace std;
using namespace __gnu_cxx;
struct trip {
int trip_id;
int delta_n;
int delta_secs;
trip(int trip_id, int delta_n, int delta_secs){
this->trip_id = trip_id;
this->delta_n = delta_n;
this->delta_secs = delta_secs;
}
};
struct hash_trip
{
size_t operator()(const trip t)
{
hash<int> H;
return H(t.trip_id);
}
};
struct eq_trip
{
bool operator()(const trip t1, const trip t2) {
return (t1.trip_id==t2.trip_id) &&
(t1.delta_n==t2.delta_n) &&
(t1.delta_secs==t2.delta_secs);
}
};
int main()
{
hash_set<trip, hash_trip, eq_trip> trips;
trip t = trip(3,2,-1);
trip t1 = trip(3,2,0);
trips.insert(t);
}
when I try to compile it, I get the following error message:
/usr/include/c++/4.2.1/ext/hashtable.h: In member function ‘size_t __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::_M_bkt_num_key(const _Key&, size_t) const [with _Val = trip, _Key = trip, _HashFcn = hash_trip, _ExtractKey = std::_Identity<trip>, _EqualKey = eq_trip, _Alloc = std::allocator<trip>]’:
/usr/include/c++/4.2.1/ext/hashtable.h:599: instantiated from ‘size_t __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::_M_bkt_num(const _Val&, size_t) const [with _Val = trip, _Key = trip, _HashFcn = hash_trip, _ExtractKey = std::_Identity<trip>, _EqualKey = eq_trip, _Alloc = std::allocator<trip>]’
/usr/include/c++/4.2.1/ext/hashtable.h:1006: instantiated from ‘void __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::resize(size_t) [with _Val = trip, _Key = trip, _HashFcn = hash_trip, _ExtractKey = std::_Identity<trip>, _EqualKey = eq_trip, _Alloc = std::allocator<trip>]’
/usr/include/c++/4.2.1/ext/hashtable.h:437: instantiated from ‘std::pair<__gnu_cxx::_Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>, bool> __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::insert_unique(const _Val&) [with _Val = trip, _Key = trip, _HashFcn = hash_trip, _ExtractKey = std::_Identity<trip>, _EqualKey = eq_trip, _Alloc = std::allocator<trip>]’
/usr/include/c++/4.2.1/ext/hash_set:197: instantiated from ‘std::pair<typename __gnu_cxx::hashtable<_Value, _Value, _HashFcn, std::_Identity<_Value>, _EqualKey, _Alloc>::const_iterator, bool> __gnu_cxx::hash_set<_Value, _HashFcn, _EqualKey, _Alloc>::insert(const typename __gnu_cxx::hashtable<_Value, _Value, _HashFcn, std::_Identity<_Value>, _EqualKey, _Alloc>::value_type&) [with _Value = trip, _HashFcn = hash_trip, _EqualKey = eq_trip, _Alloc = std::allocator<trip>]’
try.cpp:45: instantiated from here
/usr/include/c++/4.2.1/ext/hashtable.h:595: error: passing ‘const hash_trip’ as ‘this’ argument of ‘size_t hash_trip::operator()(trip)’ discards qualifiers
What am I doing wrong?
You should investigate using the STL's TR1 extension namely
unordered_map
unordered_set
unordered_multimap
unordered_mutliset
Most modern C++ compilers ship with these extensions, hence there is no need to use a non-standard class such as hash_set etc.
http://en.wikipedia.org/wiki/Unordered_map_%28C%2B%2B_class%29
http://publib.boulder.ibm.com/infocenter/comphelp/v9v111/index.jsp?topic=/com.ibm.xlcpp9.aix.doc/standlib/stl_unordered_map.htm
http://www.cplusplus.com/reference/unordered_set/unordered_set/
So close! The last error in your output reveals your hash_trip routine should be declared const:
size_t operator()(const trip t) const // note the ending 'const'
{
//...
}
You'll probably need to do the same thing for eq_trip. Also, I would recommend passing the arguments to these functions by constant reference to avoid an unnecessary copy of the data you're passing:
size_t operator()(const trip& t) const // note the '&'
{
//...
}
Related
Overview
I am implementing a producer-consumer model in C++. I have mulitple consumers, each of which gets its own thread and queue. For easy access, I want to have a mapping of form <ID, consumer instance>, so that I can simply get the consumers thread via lookup when pushing data to it. The compiler throws an error when trying to access the stored instances method.
Code
The consumer class starts a thread with an attached FIFO queue, and waits for items to get pushed in (queue header is available here: https://github.com/cameron314/readerwriterqueue/blob/master/readerwriterqueue.h )
// consumer.h
#include <functional>
#include <thread>
#include "readerwriterqueue.h"
// TYPE HAS TO BE A POINTER
template<typename T>
class Consumer
{
public:
Consumer(int id, std::function<void(int, T&)> func) :
m_id(id), m_func(func) {}
~Consumer(){
m_running = false;
this->pushBack(nullptr);
m_thread.join();
}
// satisfy rule of three; disallow copying as we manage a thread
Consumer(const Consumer&) = delete;
Consumer& operator=(const Consumer&) = delete;
void pushBack(const T& t){
m_BufferQueue.enqueue(t);
}
private:
void work() {
m_running = true;
while(m_running || m_BufferQueue.peek())
{
T t;
m_BufferQueue.wait_dequeue(t);
if (t == nullptr)
break;
m_func(m_id, t);
}
}
int m_id;
std::function<void(int, T&)> m_func;
moodycamel::BlockingReaderWriterQueue<T> m_BufferQueue{64};
std::thread m_thread{&Consumer::work, this};
std::atomic_bool m_running;
};
I want to create multiple instances of this class and place them into an unordered map, so I can push into their queues if necessary. As far as I can tell, emplace helps me to avoid writing a move constructor/assignment operator as it creates and places the object directly in the map.
// main_player.cpp
#include <iostream>
#include <unordered_map>
#include "consumer.h"
void func(int id, int* val)
{
std::cout << "Thread " << id << " received value " << *val << "\n";
}
int main(int argc, char** argv) {
std::unordered_map<int, Consumer<int*>> consumers;
consumers.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(0, func));
// push into queues here and let the threads do the work
for(int i = 0; i < 5; i++)
consumers[0].pushBack(&i)
return 0;
}
The compiler has a problem with the pushBack into a queue. From the error log, I can infer that he does seem to construct another instance and fails. Can somebody help me out here?
Error Log
In file included from /usr/include/c++/7/functional:54:0,
from ../consumer.h:4,
from ../main_player.cpp:31:
/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 = {int&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const int; _T2 = Consumer<int*>]’:
/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 = {int&&}; _Args2 = {}; _T1 = const int; _T2 = Consumer<int*>]’
/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 int, Consumer<int*> >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::pair<const int, Consumer<int*> >]’
/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 int, Consumer<int*> >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::pair<const int, Consumer<int*> >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const int, Consumer<int*> > >]’
/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<int&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const int, Consumer<int*> >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const int, Consumer<int*> >, 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 = int; _Pair = std::pair<const int, Consumer<int*> >; _Alloc = std::allocator<std::pair<const int, Consumer<int*> > >; _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 = Consumer<int*>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = int]’
/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 = int; _Tp = Consumer<int*>; _Hash = std::hash<int>; _Pred = std::equal_to<int>; _Alloc = std::allocator<std::pair<const int, Consumer<int*> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = Consumer<int*>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = int]’
../main_player.cpp:48:20: required from here
/usr/include/c++/7/tuple:1652:70: error: no matching function for call to ‘Consumer<int*>::Consumer()’
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
^
In file included from ../main_player.cpp:31:0:
../consumer.h:13:5: note: candidate: Consumer<T>::Consumer(int, std::function<void(int, T&)>) [with T = int*]
Consumer(int id, std::function<void(int, T&)> func) :
^~~~~~~~
../consumer.h:13:5: note: candidate expects 2 arguments, 0 provided
make: *** [main_player.o] Error 1
subdir.mk:66: recipe for target 'main_player.o' failed
"make -j20 all" terminated with exit code 2. Build might be incomplete.
18:01:03 Build Failed. 3 errors, 0 warnings. (took 739ms)
You need to add the default constructor in class Consumer as the error says
error: no matching function for call to ‘Consumer<int*>::Consumer()’
You can do so in 2 ways:
Method 1
// consumer.h
#include <functional>
#include <thread>
#include "readerwriterqueue.h"
// TYPE HAS TO BE A POINTER
template<typename T>
class Consumer
{
public:
Consumer(int id, std::function<void(int, T&)> func) :
m_id(id), m_func(func) {}
//other member here
//ADD THE DEFAULT CONSTRUCTOR
Consumer() = default;
private:
//...
int m_id = 0; //USE IN-CLASS INITIALIZER for built in type
//other members here
};
Method 2
// consumer.h
#include <functional>
#include <thread>
#include "readerwriterqueue.h"
// TYPE HAS TO BE A POINTER
template<typename T>
class Consumer
{
public:
Consumer(int id, std::function<void(int, T&)> func) :
m_id(id), m_func(func) {}
//other member here
//ADD THE DEFAULT CONSTRUCTOR
Consumer(): m_id(0)
{
}
private:
//...
int m_id;
//other members here
};
I am trying to declare an unordered_multiset which is going to contain objects of a custom class, but I can't find any examples out there for this. Following the documentation, it seems that I need to declare an operator== in the class, alongside the hashing function(operator() const) which, as per the documentation:
Accepts a single parameter of type Key.
Returns a value of type std::size_t that represents the hash value of the parameter.
Does not throw exceptions when called.
For two parameters k1 and k2 that are equal, std::hash<Key>()(k1) == std::hash<Key>()(k2).
For two different parameters k1 and k2 that are not equal, the probability that
std::hash<Key>()(k1) == std::hash<Key>()(k2) should be very small, approaching 1.0/std::numeric_limits<std::size_t>::max().
The code looks like this in a very simplistic way:
Class MyClass:
// comparator
bool MyClass::operator ==(const MyClass b) const {
return (string == b.getString()); // compares two strings
}
// hash operation
size_t MyClass::operator()() const {
return hash<string>()(string); // bases the hash on the string
}
main.cpp:
unordered_multiset<MyClass> s1;
// Also tried: unordered_multiset<MyClass, std::hash<MyClass>, std::equal_to<MyClass>> s1;
However, in the instatiation of the unordered_multiset the compiler (GCC) will complain about the hash functions (Tupla is the name of MyClass, and there are 1000+ lines, I included a few of them):
error: use of deleted function ‘std::unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset() [with _Value = Tupla; _Hash = std::hash<Tupla>; _Pred = std::equal_to<Tupla>; _Alloc = std::allocator<Tupla>]’
78 | unordered_multiset<Tupla> s1;
| ^~
/usr/include/c++/10/bits/unordered_set.h:949:7: note: ‘std::unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset() [with _Value = Tupla; _Hash = std::hash<Tupla>; _Pred = std::equal_to<Tupla>; _Alloc = std::allocator<Tupla>]’ is implicitly deleted because the default definition would be ill-formed:
949 | unordered_multiset() = default;
/usr/include/c++/10/bits/unordered_set.h:949:7: error: use of deleted function ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable() [with _Key = Tupla; _Value = Tupla; _Alloc = std::allocator<Tupla>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<Tupla>; _H1 = std::hash<Tupla>; _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, false>]’
In file included from /usr/include/c++/10/unordered_set:46,
/usr/include/c++/10/bits/hashtable.h:451:7: note: ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable() [with _Key = Tupla; _Value = Tupla; _Alloc = std::allocator<Tupla>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<Tupla>; _H1 = std::hash<Tupla>; _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, false>]’ is implicitly deleted because the default definition would be ill-formed:
451 | _Hashtable() = default;
An allocator is also mentioned as a pre-requisite for the unordered_multiset, but I neither understand it not I am sure whether that should be included (or how).
What did I miss?
you can replace string with your struct i guess:
#include <bits/stdc++.h>
using namespace std;
// Custom Hash Functor that will compute the hash on the
// passed string objects length
struct StringHashBySize {
public:
size_t operator()(const std::string & str) const {
int size = str.length();
return std::hash<int>()(size);
}
};
// Custom comparator that compares the string objects by length
struct StringEqualBySize {
public:
bool operator()(const std::string & str1, const std::string & str2) const {
if (str1.length() == str2.length())
return true;
else
return false;
}
};
int main() {
// Declaring unordered_multiset with Custom Hash Function and comparator
unordered_multiset<std::string, StringHashBySize, StringEqualBySize> multiset;
return 0;
}
The simplest way of doing this would probably be a helper class for the hash implementation:
struct MyClassHash
{
const std::hash<std::string> m_stringHash {};
size_t operator()(const MyClass& value) const
{
return m_stringHash(value.getString());
};
};
and use this for hashing:
std::unordered_multiset<MyClass, MyClassHash> s1;
I btw recommend passing the parameter as reference to operator==, not by copy:
bool operator==(const MyClass& other) const;
I have to return pointer to a class in a fuction
Contact* PhoneBook::SearchById(unsigned int id) {
if (contacts_.find(id) != contacts_.end()) {
return &(contacts_[id]);
}
return nullptr;
}
Here are classes definitions:
struct Contact {
public:
friend class PhoneBook;
private:
Number number_;
Name name_;
Address address_;
Contact(string number, string name, const string* address);
string GetUnifiedNumber() const;
std::vector<string> GetUnifiedName() const;
};
class PhoneBook {
public:
unsigned int AddContact(string number, string name, const string* address = nullptr);
Contact* SearchById(unsigned int id);
PhoneBook();
private:
std::unordered_map<unsigned int, Contact> contacts_;
};
I get an error when compile
In file included from /usr/include/c++/8/bits/hashtable_policy.h:34,
from /usr/include/c++/8/bits/hashtable.h:35,
from /usr/include/c++/8/unordered_map:46,
from phone_book.cpp:4:
/usr/include/c++/8/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 = {const unsigned int&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const unsigned int; _T2 = Contact]’:
/usr/include/c++/8/tuple:1657:63: required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {const unsigned int&}; _Args2 = {}; _T1 = const unsigned int; _T2 = Contact]’
/usr/include/c++/8/ext/new_allocator.h:136:4: required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const unsigned int, Contact>; _Args = {const std::piecewise_construct_t&, std::tuple<const unsigned int&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const unsigned int, Contact>, false>]’
/usr/include/c++/8/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 unsigned int, Contact>; _Args = {const std::piecewise_construct_t&, std::tuple<const unsigned int&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const unsigned int, Contact>, false>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::__detail::_Hash_node<std::pair<const unsigned int, Contact>, false> >]’
/usr/include/c++/8/bits/hashtable_policy.h:2082: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<const unsigned int&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const unsigned int, Contact>, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const unsigned int, Contact>, false>]’
/usr/include/c++/8/bits/hashtable_policy.h:711: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[](const key_type&) [with _Key = unsigned int; _Pair = std::pair<const unsigned int, Contact>; _Alloc = std::allocator<std::pair<const unsigned int, Contact> >; _Equal = std::equal_to<unsigned int>; _H1 = std::hash<unsigned 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 = Contact; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = unsigned int]’
/usr/include/c++/8/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[](const key_type&) [with _Key = unsigned int; _Tp = Contact; _Hash = std::hash<unsigned int>; _Pred = std::equal_to<unsigned int>; _Alloc = std::allocator<std::pair<const unsigned int, Contact> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = Contact; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = unsigned int]’
phone_book.cpp:109:39: required from here
/usr/include/c++/8/tuple:1668:70: error: no matching function for call to ‘Contact::Contact()’
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
^
phone_book.cpp:113:1: note: candidate: ‘Contact::Contact(std::__cxx11::string, std::__cxx11::string, const string*)’
Contact::Contact(string number, string name, const string* address) {
^~~~~~~
phone_book.cpp:113:1: note: candidate expects 3 arguments, 0 provided
phone_book.cpp:33:8: note: candidate: ‘Contact::Contact(const Contact&)’
struct Contact {
^~~~~~~
phone_book.cpp:33:8: note: candidate expects 1 argument, 0 provided
phone_book.cpp:33:8: note: candidate: ‘Contact::Contact(Contact&&)’
According to this I have to make another constructor, so I did. But compiler says it has to be public, and I don't want to make Contact constructor public. I want that Contacts can be created only by PhoneBook class. How can I solve this compilation problem?
In order to use operator[] of std::map, the value must be default constructible. Try adding a public constructor without arguments to your Contact class. You could also try to avoid usage of the operator:
Contact* PhoneBook::SearchById(unsigned int id) {
auto c = contacts_.find(id);
if (c != contacts_.end()) {
return &c->second;
}
return nullptr;
}
The variable contacts_ is a std::map.
You can pass copies of map items.
You can pass iterators of map items.
The operator[] does not work the same with a std::map as it does with an array. The operator[] method is a search function. See std::map.
So, the expression &contacts[i] for a std::map doesn't function the same as an array.
My suggestion is to change the signature of the function:
bool Phonebook::SearchById(int id, Contact& c)
{
const std::map<unsigned int, Contact>::const_iterator iter = contacts_.find(id);
if (iter != contacts_.end())
{
c = iter->second;
}
return iter != contacts_.end();
}
As it has been noted, map operator [] searchs for item and create it if item is not found. The default public constructor is required for that. If you use contacts_.find approach the class instances are already created and constructor is not required so it works well.
But how contacts_ can be filled? I suppose it should be the same issue of public default constructor. The solution can be
friend struct std::pair<const unsigned int, Contact>;
directly after
friend class PhoneBook;
That allows access to constructor for pair which is used in map and responsible for instance creation. But you grant access to constructor for one more class...
Alternatively (i.e. the friend is only PhoneBook) you can use pointer or smart pointer to keep contacts:
std::unordered_map<unsigned int, std::unique_ptr<Contact> > contacts_;
The negative is you need to create instances of Contact manually since contacts_[ID] can create only empty pointer which needs to be filled.
Please remember usage of friend is symptom that you need to redesign classes.
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 need to insert a 1D array into the hashset.
But I got error while compiling.
#include <stdio.h>
#include <stdlib.h>
#include <hash_set.h>
using namespace std;
int hash_comp(const int* state1,const int* state2) {
int result = 0;
for (i = 0; i < 16; i++)
{
if (state1[i] != state2[i]) {
result = -1;
}
}
return result;
}
struct eqArray
{
bool operator()(const int* a1,const int* a2) const
{
return hash_comp(a1,a2) == 0;
}
};
hash_set<int*,hash<int*>,eqArray> closelist;
int main(int argc, char** argv)
{
const int sn[16] = {1,2,3,4,5,6,0,8,9,10,11,12,13,14,7,15};
closelist.insert(sn);
return 0;
}
/usr/include/c++/4.2.1/ext/hashtable.h: In member function 'size_t __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::_M_bkt_num_key(const _Key&, size_t) const [with _Val = int*, _Key = int*, _HashFcn = __gnu_cxx::hash<int*>, _ExtractKey = std::_Identity<int*>, _EqualKey = std::equal_to<int*>, _Alloc = std::allocator<int*>]':
/usr/include/c++/4.2.1/ext/hashtable.h:599: instantiated from 'size_t __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::_M_bkt_num(const _Val&, size_t) const [with _Val = int*, _Key = int*, _HashFcn = __gnu_cxx::hash<int*>, _ExtractKey = std::_Identity<int*>, _EqualKey = std::equal_to<int*>, _Alloc = std::allocator<int*>]'
/usr/include/c++/4.2.1/ext/hashtable.h:1006: instantiated from 'void __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::resize(size_t) [with _Val = int*, _Key = int*, _HashFcn = __gnu_cxx::hash<int*>, _ExtractKey = std::_Identity<int*>, _EqualKey = std::equal_to<int*>, _Alloc = std::allocator<int*>]'
/usr/include/c++/4.2.1/ext/hashtable.h:437: instantiated from 'std::pair<__gnu_cxx::_Hashtable_iterator<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>, bool> __gnu_cxx::hashtable<_Val, _Key, _HashFcn, _ExtractKey, _EqualKey, _Alloc>::insert_unique(const _Val&) [with _Val = int*, _Key = int*, _HashFcn = __gnu_cxx::hash<int*>, _ExtractKey = std::_Identity<int*>, _EqualKey = std::equal_to<int*>, _Alloc = std::allocator<int*>]'
/usr/include/c++/4.2.1/ext/hash_set:197: instantiated from 'std::pair<typename __gnu_cxx::hashtable<_Value, _Value, _HashFcn, std::_Identity<_Tp>, _EqualKey, _Alloc>::const_iterator, bool> __gnu_cxx::hash_set<_Value, _HashFcn, _EqualKey, _Alloc>::insert(const typename __gnu_cxx::hashtable<_Value, _Value, _HashFcn, std::_Identity<_Tp>, _EqualKey, _Alloc>::value_type&) [with _Value = int*, _HashFcn = __gnu_cxx::hash<int*>, _EqualKey = std::equal_to<int*>, _Alloc = std::allocator<int*>]'
src/ods2.cpp:677: instantiated from here
If you use a std::array<int, 16> instead of int*, all your problems will go away. If you have no C++11 compiler, you can use boost::array instead. Also, replace hash_set with unordered_set.
It appears there is no specialization of std::hash for std::arrays, so I wrote my own:
#include <unordered_set>
#include <array>
namespace std
{
template<typename T, size_t N>
struct hash<array<T, N> >
{
typedef array<T, N> argument_type;
typedef size_t result_type;
result_type operator()(const argument_type& a) const
{
hash<T> hasher;
result_type h = 0;
for (result_type i = 0; i < N; ++i)
{
h = h * 31 + hasher(a[i]);
}
return h;
}
};
}
std::unordered_set<std::array<int, 16> > closelist;
int main()
{
std::array<int, 16> sn = {1,2,3,4,5,6,0,8,9,10,11,12,13,14,7,15};
closelist.insert(sn);
}
You didn’t post the actual error message, only the trace. That said, it’s probably because your set uses non-const int* while your data is const.
That said, I’d suggest using unordered_set instead of hash_set(either from std::tr1 if your compiler supports that, or from std if your compiler supports C++11 or from Boost), and heed Fred’s advice of using an {std,boost}::array instead of a raw pointer.
I don't think that there exists a specialized hash<int*>. I added this:
namespace __gnu_cxx{ //I'm not sure what compiler version you used,
//mine wanted this
template<>
struct hash<const int*>{
size_t operator()(const int*a) const{
size_t r = 0;
for (int i=0;i<16;i++)
r = (r<<1) ^ a[i]; //not sure if it makes sense as a hash.
return r;
}
};
}
I also put in some consts and it compiles.