Is there a short-cut to using std::unordered_set with classes that implement operator== and hash? Specifically, is there a way to (1) avoid creating a stand-alone operator==(const Object& a, const Object& b) function, and (2) avoid defining an entire class just to hold size_t operator()(const Object& o) const {return o.hash();}
Of course, neither of these are problems, I'm just curious.
operator== being defined as a member function is already catered for.
If the class being used as a key has a member function hash() const then we can do something simple like this:
-
#include <unordered_map>
#include <string>
struct myclass {
std::size_t hash() const { return 0; }
bool operator==(const myclass& r) const { return true; }
};
struct self_hash
{
template<class T>
auto operator()(const T& r) const { return r.hash(); }
};
int main()
{
using mymap = std::unordered_map<myclass, std::string, self_hash>;
auto m = mymap();
}
Related
So, I have a pointer wrapper class which stores only a pointer and I need to use this class instance as a key in an unordered map. I currently have a similar setup with this pointer wrapper instances as keys to a std::map by overriding bool operator< but for an unordered map setup I would need to override two other operators == and (). I've figured how == operator implementation would be. but not sure about an implementation for the () operator. what sort of hash implementation setup should I be doing in this case? I've checked in places and most examples cover non pointer cases and they use two Key items and form hash for each and compare them for () implementation.
template <class T>
class PointerWrap{
public:
T* pointer;
bool operator<(const PointerWrap& other)const{return *pointer < *other.pointer;}
bool operator==(const PointerWrap& other)const{return *pointer == *other.pointer;}
//size_t operator()(const PointerWrap& other)const{return (*pointer)(*other.pointer);}
};
class VarType{
bool operator<(const VarType& other)const{return this < &other;}
bool operator==(const VarType& other)const{return this == &other;}
size_t operator()(const VarType& other)(.?.?.}
};
//Desired setup.
std::unordered_map<PointerWrap<VarType>,Value> mymap;
Since you seem to need a hash function only for using PointerWrappers in an unordered map, the hash function in the standard library should serve you well. (But these are not cryptographically secure hash functions so don't use them for anything else). Here is some code to show how to do this:
#include <unordered_map>
#include <iostream>
template <class T>
class PointerWrap {
public:
T* pointer;
bool operator<(const PointerWrap& other)const { return *pointer < *other.pointer; }
bool operator==(const PointerWrap& other)const { return *pointer == *other.pointer; }
size_t operator()(const PointerWrap& other) const {return (*pointer)(*other.pointer);}
};
class VarType {
public: // PointerWrap has no access to these operators without a public access specifier
bool operator<(const VarType& other)const { return this < &other; }
bool operator==(const VarType& other)const { return this == &other; }
// Pointless to hash a object without any data
std::size_t operator()(const VarType& other)const {
return 0;
}
};
// Specialization of std::hash for PointerWrap<T>
template<typename T>
class std::hash<PointerWrap<T>> {
public:
size_t operator()(PointerWrap<T> v) const{
return std::hash<T*>()(v.pointer);
}
};
int main() {
// your desired setup compiles.
std::unordered_map<PointerWrap<VarType>, int> mymap;
PointerWrap<VarType> a;
mymap[a] = 5;
std::cout << mymap[a] << std::endl;
return 0;
}
I have a C++ code that currently looks like this: there is a class hierarchy to do perform some comparison and a list class that uses it. Which comparison operation to use is determined at runtime based on some schema object. Here is the structure:
class A{
bool doComparison(const string& s1, const string& s2) const=0;
}
class B: public A{
bool doComparison(const string& s1, const string& s2) const {
...
}
}
class C: public A{
bool doComparison(const string& s1, const string& s2) const {
...
}
}
template <class, S>
public FancyList{
shared_ptr<A> z_;
vector<S> v;
FancyList(shared_ptr<A> z) : z_(z);
void DoSmth(){
....
z_->doComparison(arg1, arg2);
}
}
typedef FancyList<string> FancyStringList;
// Determine which comparison to use at runtime
shared_ptr<A> c = nullptr;
switch(type):
case int:
c = make_shared<B>();
break;
case double:
c = make_shared<B>();
break;
FancyStringList l(c);
l.push_back("stuff");
C# used to be my main language so this code seemed ok to me. But I was told that the problem with this approach is that it uses virtual functions so there is a slight overhead in a method call. What is the proper C++-way of reorganizing this code so there is no need to have this class hierarchy and no need to use virtual functions?
Contrary to what you want, the overhead of virtual function is unavoidable because the decision of which actual function is called is made in runtime.
If the decision is always made in runtime, the compiler cannot hard-code the function call into the generated machine code. It has to be a indirect function call: to use a pointer to point to a function, and to dereference the pointer before the function call. Virtual function is just one way to do indirect function call.
Template is a way tell the compiler to generate code during compile-time. All template can do is to not introduce overhead when the decision is made during compile-time. It can't help you remove works that must be done in runtime.
If you are still interested in using template, you may consider having the comparator as a template parameter.
template <class T, class Comparator>
class MyList
{
std::vector<T> vec;
Comparator comp;
public:
void do_thing(const T& a, const T& b)
{
vec.push_back(a);
vec.push_back(b);
bool x = comp(vec[0], vec[1]); // for example
std::cout << x;
}
};
In the comparator class, overload the function call operator.
class Compare1
{
public:
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return lhs < rhs;
}
};
class Compare2
{
public:
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return lhs.size() < rhs.size();
}
};
int main()
{
MyList<std::string, Compare1> myli1;
myli1.do_thing("a", "b");
MyList<std::string, Compare2> myli2;
myli2.do_thing("c", "d");
}
You can even hide indirect function call behind comparator class. But it does not remove the overhead.
class A
{
public:
virtual bool doComparison(const std::string& s1, const std::string& s2) const=0;
virtual ~A() = default;
};
class PolymorphicComparator
{
private:
std::shared_ptr<A> comp;
public:
PolymorphicComp(std::shared_ptr<A> c) : comp(c) {}
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return comp->doComparison(lhs, rhs);
}
};
I want to populate a std::set of GraphNode objects and check if another GraphNode with the same value exists in the set. In Java, objects can be compared by overloading equals and compareTo methods, instead of creating some functor object. I implemented operator==(T& t) and expected to find the object in the set like this,
std::find(nodesSet->begin(),nodesSet->end(), new GraphNode<T>(1))!=nodesSet->end())
But I am not getting the break point in neither == nor ()() operator functions. Why is it so? Is there a way to find the object by object comparison?
template<class T>
class GraphNode
{
friend class Graph<T>;
friend bool operator==(GraphNode<T>& node1, GraphNode<T>& node2);
private:
T t;
std::vector<GraphNode<T>*> adjNodes;
public:
bool operator==(T& t);
};
template<class T>
inline bool GraphNode<T>::operator==(T & t)
{
return this->t == t ? true : false;
}
template<class T>
inline bool operator==(GraphNode<T>& node1, GraphNode<T>& node2)
{
return node1.t == node2.t ? true : false;
}
void populate()
{
std::set<GraphNode<T>*>* nodesSet = new set<GraphNode<T>*>;
nodeSet->insert(new GraphNode<T>(1));
nodeSet->insert(new GraphNode<T>(2));
if ( std::find( nodesSet->begin(),nodesSet->end(),
new GraphNode<T>(1) ) != nodesSet->end() )
{
cout<<"found value";
}
}
As aschepler pointed out, the problem with your code is that you end up comparing pointers, not objects. std::find (look at the possible implementations in the linked page), if called without a predicate, uses the == operator to compare what is returned when the iterators you give it are dereferenced. In your case, you have a std::set<GraphNode<T>*> nodesSet, so the type of *nodesSet.begin() is GraphNode<T>*, not GraphNode<T> (note the lack of star). In order for you to be able to use the == operator defined for your GraphNode, you need to have your set be std::set<GraphNode<T>>, that is of objects of your type rather than of pointers.
If you have to store pointers in your set (e.g. because you don't want to copy the objects), you can write a wrapper for pointers that uses the comparison operator for the underlying class of the pointers. Here's an example:
#include <iostream>
#include <set>
#include <algorithm>
class obj {
int i;
public:
obj(int i): i(i) { }
bool operator<(const obj& o) const { return i < o.i; }
bool operator==(const obj& o) const { return i == o.i; }
int get() const { return i; }
};
template <typename T>
class ptr_cmp {
T* p;
public:
ptr_cmp(T* p): p(p) { }
template <typename U>
bool operator<(const ptr_cmp<U>& o) const { return *o.p < *p; }
template <typename U>
bool operator==(const ptr_cmp<U>& o) const { return *o.p == *p; }
T& operator*() const { return *p; }
T* operator->() const { return p; }
};
int main(int argc, char* argv[])
{
obj five(5), seven(7);
std::set<ptr_cmp<obj>> s;
s.insert(&five);
s.insert(&seven);
obj x(7);
std::cout << (*std::find(s.begin(),s.end(), ptr_cmp<obj>(&x)))->get()
<< std::endl;
return 0;
}
It turned out that my compiler (gcc 6.2.0) required both operator== and operator< for std::find to work without a predicate.
What is wrong with using a predicate though? It is a more generalizable approach. Here's an example:
#include <iostream>
#include <set>
#include <algorithm>
class obj {
int i;
public:
obj(int i): i(i) { }
bool operator==(const obj& o) const { return i == o.i; }
int get() const { return i; }
};
template <typename T>
struct ptr_cmp {
const T *l;
ptr_cmp(const T* p): l(p) { }
template <typename R>
bool operator()(const R* r) { return *l == *r; }
};
template <typename T>
ptr_cmp<T> make_ptr_cmp(const T* p) { return ptr_cmp<T>(p); }
int main(int argc, char* argv[])
{
obj five(5), seven(7);
std::set<obj*> s;
s.insert(&five);
s.insert(&seven);
obj x(7);
std::cout << (*std::find_if(s.begin(),s.end(), make_ptr_cmp(&x)))->get()
<< std::endl;
return 0;
}
Note, that make_ptr_cmp allows you to avoid explicitly stating the type, so you can write generic code.
If you can use C++11, use can just use a lambda function instead of ptr_cmp,
std::find_if(s.begin(),s.end(), [&x](const obj* p){ return *p == x; } )
std::find compares the values pointed at by the iterators. These values are pointers, not objects. So none of them will be equal to new GraphNode<T>(1), which is a brand new pointer to a brand new object.
As others have stated, you are comparing pointers, which won't work as expected, it's doing comparisons on addresses in memory. The operation a < b has a valid meaning for a pointer but will order the elements by their location in memory, not on their contained data elements and also no elements will be unique, as they will all have unique addresses. That is unless you try to insert the same element twice.
The above issue however will be hidden by using std::find, which iterates over all the elements in the container anyway. If you are using a set, you should be aspiring to get logarithmic time look ups for elements, so should use sets own find function, which knows that its a binary tree under the hood.
In C++, the equivalent of Object#equals is operator== (as you knew) and in the context of associative containers the equivalent of Object#compareTo is operator<. Object#equals and operator== work in the same way, exactly as you expect; If somethings equal its equal, simple to understand. Object#compareTo and operator< are used by algorithms in different ways, operator< is used to implement strict weak ordering to determine if one element is less than or greater than another.
So to allow your elements to be usable in a set you will need an overridden operator< in your GraphNode class. Once you have this you can use the std::set::find function to find elements in your set and it will find them in O(log n) time rather than linear time.
These algorithms are designed on the assumption they are working on value types, i.e not pointers but those things that are pointed to. So to use pointers you need to define a new comparison function that basically dereferences the pointers before applying the comparison (either == or <).
Some example code
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
template<typename>
class Graph
{
};
template<class T>
class GraphNode
{
friend class Graph<T>;
friend bool operator==(const GraphNode<T>& a, const GraphNode<T>& b);
private:
T t;
std::vector<GraphNode<T>*> adjNodes;
public:
explicit GraphNode(const T& tval)
:t(tval)
{}
T& getT(){ return t; }
const T& getT() const { return t; }
bool operator==(const T& t);
friend bool operator<(const GraphNode& a, const GraphNode& b){
return a.t < b.t;
}
};
template<class T>
inline bool GraphNode<T>::operator==(const T& t)
{
return (this->t == t);
}
template<class T>
inline bool operator==(const GraphNode<T>& a, const GraphNode<T>& b)
{
return (a.t == b.t);
}
int main()
{
using IntGraphNode = GraphNode<int>;
std::set<IntGraphNode> nodesSet;
nodesSet.insert(IntGraphNode(1));
nodesSet.insert(IntGraphNode(2));
auto findit = nodesSet.find(IntGraphNode(1));
if(findit != nodesSet.end())
{
std::cout << "found value\n";
}
auto findit2 = std::find_if(
nodesSet.begin(),
nodesSet.end(),
[](IntGraphNode i) { return i.getT() == 1;});
if(findit2 != nodesSet.end())
{
std::cout << "found value aswell\n";
}
}
The first search uses sets own find function and the second uses std::find_if, which takes a predicate (function that returns either true or false) to test equality. The second example also removes the need to make a dummy object, by exposing the T object and using that in the comparison lambda function.
Also a comment about
std::find(nodesSet->begin(),nodesSet->end(), new GraphNode<T>(1))!=nodesSet->end())
There are quite a few conceptual misunderstandings in this line. Firstly std::find does not take a comparison function, that would be std::find_if, but the compiler will tell you that (in its own especially indirect and verbose way). Also the comparison function is evaluated in the algorithm, you are trying to evaluate it at the call site. The other thing is unlike java, you can't just fire off newed objects into oblivion. That's a memory leak, you no longer have any variable storing the newed value, so you can't delete it.
I want to construct two priority queue that has different compare method(there are two reverse priority methods named cmp1 and cmp2)
My program can't go through the compiler check.Why does such error happen and is there any better solution?
#include <iostream>
#include <queue>
#include <string>
using namespace std;
struct item
{
string name;
string sex;
string id;
double score;
friend istream& operator >> (istream &is,item& data)
{
is>>data.name>>data.sex>>data.id>>data.score;
}
/*friend bool operator < (item& a,item& b)
{
return a.score<b.score;
}*/
};
struct cmp1{
operator bool()(item& x,item& y)
{
return x.score>y.score;
}
};
struct cmp2
{
operator bool()(item& x,item& y)
{
return x.score<y.score;
}
};
int main()
{
priority_queue<item,vector<item>,cmp1> boys;
priority_queue<item,vector<item>,cmp2>girls;
item temp;
int num;
cin>>num>>temp;
for(int i=0;i<num;i++)
{
if(temp.sex=="M")
boys.push(temp);
else
girls.push(temp);
}
return 0;
}
I'm going to take a wild guess at the problem.... your comparison functors are incorrect. Instead of overloading operator bool, you need to overload the function call operator, i.e.
struct cmp1{
bool operator()(const item& x, const item& y)
{
return x.score>y.score;
}
};
struct cmp2
{
bool operator()(const item& x, const item& y)
{
return x.score<y.score;
}
};
(Perhaps this was what you intended, but just got the syntax a little wrong?)
Actually, I think the best way to do it is to use std::less and std::greater. If you have overloaded operator< and operator> for your class, you can do it like this:
std::priority_queue<item, std::vector<item>, std::greater<item>> boys;
std::priority_queue<item, std::vector<item>, std::less<item>> girls;
That way you don't have to write the functor. Don't forget to #include <functional>.
Also, don't forget that the operators have to be overloaded taking const ref arguments (you can also take them by value, but that's not usually a good idea), and as const methods, like in:
bool operator<(const item& i) const {
return value < i.value;
}
bool operator>(const item& i) const {
return value > i.value;
}
Change your operator functions to this:
struct cmp1{
bool operator()(item& x,item& y)
{
return x.score>y.score;
}
};
struct cmp2
{
bool operator()(item& x,item& y)
{
return x.score<y.score;
}
};
You have defined the methods in the comparison classes wrong.
Try the following code:
struct cmp1{
bool operator()(item& x,item& y)
{
return x.score>y.score;
}
};
You have defined the methods as operator bool() ....
Also it is nice to add const to the parameters, as const item& x for showing that you won't change their values, as well a const at the end of the function definition:
bool operator()(const item& x, const item& y) const {...}
for showing that you won't change the member fields too.
I will write the third correct version of the predcate
struct cmp1
{
bool operator()( const item &x, const item &y ) const
{
return x.score > y.score;
}
};
struct cmp2
{
bool operator()( const item &x, const item &y ) const
{
return x.score < y.score;
}
};
why i need to use the const function in the less traits?
for example, why i must use const in Age or ID member function.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
#include <set>
using namespace std;
class Person
{
public:
Person(int id, string name, int age):m_iID(id), m_strName(name), m_iAge(age){};
int Age() const {return m_iAge;}
int ID() const {return m_iID;}
void Display();
friend ostream& operator<<(ostream& out, Person& person);
private:
int m_iID;
string m_strName;
int m_iAge;
};
void Person::Display()
{
cout<<m_iID<<" "<<m_strName<<" "<<m_iAge<<endl;
}
ostream& operator<< (ostream& out, Person& person)
{
out<<person.m_iID<<" "<<person.m_strName<<" "<<person.m_iAge<<endl;
return out;
}
int SumPersonAge(int iSumAge, Person& person)
{
return iSumAge + person.Age();
}
template <typename Type>
void Display(Type t1)
{
cout<<t1<<endl;
}
class LessPerson
{
public:
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
};
int main()
{
set<Person, LessPerson> s1;
Person p1(1234, "Roger", 23);
Person p2(1235, "Razor", 24);
s1.insert(p1);
s1.insert(p2);
for_each(s1.begin(), s1.end(), Display<Person>);
}
if i remove the const the keyword in Age or ID function, the compiler will report me Error cannot convert 'this' pointer from 'const Person' to 'Person &'.
The answer is that the set will pass two const reference to Person objects to your comparator, and you cannot call a non-const function on a const variable.
You might be surprised as there seems not to be any const in the declaration of the functor:
struct LessPerson
{
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
};
But there is, it is just not explicit in the code. Inside the set implementation there are two const Person& references (call them r1, r2) and a LessPerson comparator (call it compare) and the code does something in the lines of if ( comparator(r1,r2) ). The compiler finds the templated operator() and tries to infer the types ending up with the type substitution: Type == const Person.
Why does the set use const references rather than plain modifiable references? Well, that is a different issue. The set is implemented as a sorted balanced tree, with each node containing the key. Now because a change in the key would break the order invariant, the keys are themselves const objects, ensuring that you cannot break the invariants.
That const keyword means that the function does not modify its object, this. Only const functions may be called from const objects. So, the compiler is telling you that you are trying to call a non-const member function from a const object.
You appear to be avoiding the const keyword, but it creeps in when the library calls your template function:
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
Type is passed as const Person.
const is not easy to get rid of without cheating, and it will always creep in. It's better to go with the flow and add const everywhere you take a reference that doesn't change an object (which is most of them).
Because the type passed to LessPerson is const Person.
So basically, LessPerson is roughly
class LessPerson
{
public:
bool operator()(const Person& t1, const Person& t2)
{
return t1.ID() < t2.ID();
}
};
You can't call non-const methods on const objects. It's a rule of C++.