Overloading operator() for a new struct - c++

I defined a new struct in c:
typedef struct TypedObject {
ObjectType type;
void * value;
} TypedObject;
where ObjectType is an enum:
typedef enum ObjectType {
type_node,
type_int,
type_error,
} ObjectType;
I would like to create a c++ set of TypedObject pointers and from previous questions I understood that I need to overload operator() in order to compare between TypedObject pointers inserted into the set.
Therefore I did it as follows:
#ifdef __cplusplus
typedef struct {
bool operator() (const TypedObject *lhs, const TypedObject *rhs){
return (lhs->type==rhs->type) && (lhs->value==rhs->value);
}
} ObjComparator;
#endif
Assume I define a set:
std::set<TypedObject *, ObjComparator> mySet;
You can assume that I use an iterator to iterate though the set.
I want to insert TypedObject x into the set. I use mySet.insert(&x) to insert its address.. but once I use mySet.find(&x), it fails to find x. The call to operator() is made but the comparison is not made as expected.
Any idea what the problem might be with the way I overloaded operator()? what am I doing wrong?
Also, Should I overload a different version of operator like < or ==?

The Comparator class you provide should implement an order comparison, so that std::set could use it to build a binary search tree.
That means that your operator() should not be symmetrical - by default it is a "less than" comparison.
In general, operator() of Comparator class should represent a strict order relation for your class, so it should be
transitive - C(a,b) && C(b,c) means C(a,c)
antisymmetrical - C(a,b) means !C(b,a)
total - !C(a,b) && !C(b,a) means "a and b are equal"
The last definition of "equality" is what std::set uses when you call set::find.
Solution: While you surely can come up with some ordering that will satisfy the rules above, perhaps you can do with some refactoring instead.
If your TypedObjects have "address identity" (i.e. any object is only equal to itself), then you can just use the default comparison - it works perfectly for pointers:
std::set<TypedObject *> mySet;
If you need to compare the members after all, the usual approach would be something like this:
bool operator() (const TypedObject *lhs, const TypedObject *rhs)
{
if(lhs->value < rhs->value) return true;
if(rhs->value < lhs->value) return false;
return (lhs->type < rhs->type)
}
Notice how it only falls back on operator< for members. In fact, it would probably be better to define operator< to compare TypedObjects, and then call it from the pointer Comparator.
Finally, if your set owns the objects (i.e. objects are destroyed upon leaving the set) then perhaps it's better to just use
std::set<TypedObject> mySet;
with the operator< overloaded for TypedObject. You will still be able to get pointers to objects from the set and use them in your C APIs, and you won't need to deal with extra comparator class and memory management.

Your order comparison is wrong as based on equality it will not be able to create BST so correct code should be like below (note < in comparator)
typedef enum ObjectType {
type_node,
type_int,
type_error,
} ObjectType;
typedef struct {
ObjectType type;
void * value;
} TypedObject;
typedef struct {
bool operator() (const TypedObject *lhs, const TypedObject *rhs){
return (lhs->type==rhs->type) && (lhs->value<rhs->value);
}
} ObjComparator;
int main()
{
std::set<TypedObject *, ObjComparator> mySet;
TypedObject obj;
obj.type=type_int;
obj.value=(void*)new int;
//*(obj.value)=4;
auto insert = mySet.insert(&obj);
std::cout<<insert.second<<std::endl;
if(mySet.find(&obj) == mySet.end())
{
std::cout<<"Not Found..."<<std::endl;
}
else
{
std::cout<<"Found..."<<std::endl;
}
return 0;
}

Related

Sort a list of Objects by given list of Names that exist [duplicate]

I am having trouble sorting a list of custom class pointers. The class I need to sort are events. These get assigned a random time and I need to do them in the right order.
#include <list>
Class Event{
public:
float time; // the value which I need to sort them by
int type; // to indicate which event i'm dealing with
Event(float tempTime, int tempType)
{
time = tempTime;
type = tempType;
}
int main(){
std::list<Event*> EventList;
list<Event*>::iterator it;
.........
If you could help me sort this out it would be much appreciated! I've been stuck on this for hours now.
Thanks!
Since the list contains pointers, rather than objects, you'll have to provide a custom comparator to compare the objects they point to. And since you're using a list, you have to use its own sort method: the generic std::sort algorithm only works on random-access sequences.
EventList.sort([](Event * lhs, Event * rhs) {return lhs->time < rhs->time;});
or, if you're stuck in the past and can't use lambdas:
struct CompareEventTime {
bool operator()(Event * lhs, Event * rhs) {return lhs->time < rhs->time;}
};
EventList.sort(CompareEventTime());
If the list contained objects (as it probably should), then it might make sense to provide a comparison operator instead:
bool operator<(Event const & lhs, Event const & rhs) {return lhs.time < rhs.time;}
std::list<Event> EventList;
//...
EventList.sort();
You should to that with std::sort. You can either make a custom comparator function that you pass as third argument to the std::sort function, or you can make a < operator overload for your class and std::sort will work naturally.

C++ std::set uniqueness override

How does the std::set<T> container check if two objects are unique? I tried overriding the equality operators (==), but it didn't work.
The reason I want to do this is that I have let's say a class Person and I specify that my Person is the same person if they have the same name (maybe even birthdate, address, etc.).
In ccpreference.com, they write the following (which is a bit unclear to me):
Everywhere the standard library uses the Compare concept, uniqueness
is determined by using the equivalence relation. In imprecise terms,
two objects a and b are considered equivalent (not unique) if neither
compares less than the other: !comp(a, b) && !comp(b, a).
I assume, that this question also expands to other STL containers and even algorithms (maybe even to the whole STL). So if in future, I want to use the function std::find, I would be looking up the name of the person and not the object itself. Is this correct?
EDIT
I want to add some example code.
// My operator overloading comparing two strings.
bool operator==(Node & rhs) const {
return this->name.compare(rhs.name);
}
Then, in the UnitTest I add twice an object with the same name into the set. It is added twice (but should be the same according to the operator==.
void test_adding_two_identical_nodes() {
// The pool is a set<Node> inside
model::Node_Pool pool{};
pool.store_node(model::Node{"Peter"});
pool.store_node(model::Node{"Peter"});
// Should be only 1 because the same node should be added once into a set.
ASSERT_EQUAL(1, pool.size());
}
std::set<T> doesn't compare using ==. It compares, by default, using std::less<T>. In turn std::less<T> uses, by default, the operator <.
One way to implement a set is to override operator<, like so:
#include <set>
#include <cassert>
struct Person {
const char *name;
int uid;
};
bool operator<(const Person& a, const Person& b) {
return a.uid < b.uid;
}
int main () {
Person joe = {"joseph", 1};
Person bob = {"robert", 2};
Person rob = {"robert", 3};
Person sue = {"susan", 4};
std::set<Person> people;
people.insert(joe);
people.insert(bob);
people.insert(rob);
assert(people.count(joe) == 1);
assert(people.count(bob) == 1);
assert(people.count(rob) == 1);
assert(people.count(sue) == 0);
Person anonymous_3 = {"", 3};
assert( std::strcmp(people.find(anonymous_3)->name, "robert") == 0);
}
Alternatively, one can pass a compare operator as a template parameter when declaring the set. In the example above, this might be the compare operator:
struct Person_Compare {
bool operator()(const Person& a, const Person& b) const {
return a.uid < b.uid;
}
};
And the std::set declaration might look like this:
std::set<Person, Person_Compare> people;
The rest of the example is unchanged.
First of all, don't override comparison operators to compare anything but TOTAL equivalence. Otherwise you end up with a maintenance nightmare.
That said, you'd override operator <. You should instead give set a comparitor type though.
struct compare_people : std::binary_function<person,person,bool>
{
bool operator () ( person const& a, person const& b) const { return a.name() < b.name();
};
std::set<person, compare_people> my_set;

operator== not working in template function

I am writing a template function which takes in a vector and a struct, and inserts that struct into the vector. If there is a duplicate struct in the vector however,the function will not insert the struct because the values must all be unique. In order to do this, I am using the find function of the STL library, and analyzing the return value using the operator==. However, I am getting this error every time I try and compile:
error: no match for 'operator==' (operand types are 'OneToOneMap' and'OneToOneMap')|
My template function can be seen below:
template<typename Type> void AddToList(vector<Type> add_to, Type to_insert){
bool contains_element = *find( add_to.begin(), add_to.end(), to_insert) == *add_to.end() ? false:true;
if(contains_element){
cout << "Element is already in list" << endl;
}else{
add_to.push_back(to_insert);
}
}
The question isn't entirely clear however I suspect you haven't overloaded the operator== in the class/struct OneToOneMap which you are trying to compare in your code. Assuming this is a user-defined type, overload this operator (and operator!=) as follows:
class OneToOneMap {
public:
//...
bool operator==(const OneToOneMap& _other) const {
// do comparison based on fields
}
bool operator!=(const OneToOneMap& _other) const {
return !(*this == _other);
}
};
Edit
Ineed, you need to provide an overload for the type that you want to use std::find<T>() on! The reason is that of course the function needs a way to compare the container elements to find out if they're equal or not. Provide an overload for the bool T::operator==(const T& other) as ArchbishopOfBanterbury has noticed.
(To be more exact, comparison using the bool operator==(...) is used when the user has not provided another predicate to compare the elements, see http://en.cppreference.com/w/cpp/algorithm/find)
Original answer:
Remove the unnecessary dereference operators * when comparing iterators:
bool contains_element = find( add_to.begin(), add_to.end(), to_insert) == add_to.end() ? false:true;
You don't need the false : true either, since the comparison returns a bool:
bool contains_element = find( add_to.begin(), add_to.end(), to_insert) == add_to.end();
The logic is that the std::find<T>() function returns an iterator, and you compare that iterator to the vector<T>::end() iterator, i.e. the "null" iterator, to check if find<T>() has been able to find anything or not. You don't need to compare the T values themselves.

Avoiding Helper Functions for Doing Comparisons

Say I have a type with a member function:
class Thing {
std::string m_name;
public:
std::string & getName() {
return m_name;
}
};
And say I have a collection of that type:
std::vector<Thing> things;
And I want to keep the things in order by name. To do that, I use std::lower_bound to figure out where to put it:
bool thingLessThan(Thing const& thing, std::string const& name) {
return thing.getName() < name;
}
void addThing(std::string const& name) {
vector<Thing>::iterator position = lower_bound(
things.begin(), things.end(),
name,
thingLessThan);
if (position == things.end() || position->getName() != name) {
position = things.insert(position, Thing());
position->getName() = name;
}
}
Is there a way to do the same thing as the thingLessThan function without actually creating a function, perhaps using std::mem_fun, std::less, etc?
Other than a lambda you can simply define an operator< which adheres to strict weak ordering to allow a container of your object to be comparable by STL algorithms with the default predicate std::less
class whatever
{
public:
bool operator<(const whatever& rhs) const { return x < rhs.x; }
private:
int x;
};
std::vector<whatever> v;
std::sort(v.begin(), v.end());
Sure. You can use a lambda expression (assuming your compiler supports it):
vector<Thing>::iterator position = lower_bound(
things.begin(), things.end(),
name,
[](Thing const& thing, std::string const& name) { return thing.getName() < name; });
Of course, an alternative option is just to define operator< for the class, then it will be used by default, if you don't specify another comparer function for std::lower_bound.
Depending on what your purpose is? If you just like the syntactic niceness of not declaring something to be used in one place, use lambda expressions to create an anonymous function.
You can overload operator<() and use std::less<T> if you don't want to write predicates contantly. Also you can use lambda-expressions, which would be much nicer, because operator<() is logically connected only with things, that can be put in some order in obvious ways, like numbers or strings.
If you use a std::map, the strings will be placed in alphabetical order automatically. If you want to modify the ordering further, create your own key comparison function. I think this would be the simplest option.
To use a std::list, you can write your own comparison code inside of the addThing() function that goes through the list looking at each string and inserts the new one at the appropriate place.

How can I sort a list when the sorting criterion requires an extra variable? C++

this is for an assignment so I will be deliberately general. My question is related to implementation decisions I already made--maybe they weren't good ones.
I have a list of pointers to structs, e.g. list<MyStruct*> bob; At one point I've needed to sort these pointers by one of the data members of their targets and I was able to do that easily with
bool sortbyarrival(const MyStruct* a, const MyStruct* b) {
return a->arrival < b->arrival;
}
And then calling bob.sort(sortbyarrival); Works great.
Now somewhere else I need to sort by a different criterion, which involves a counter in the program. I need something like return counter*a->arrival < counter*b->arrival; But the way I just described is the only way I know how to do a sort, I think, and I don't know how to pass my counter as an additional argument. How can I sort this list of pointers?
ETA: The counter is just a variable in main. So ideally I could call something like bob.sort(sortbyratio, counter); or sort(bob.begin(), bob.end(), sortbyratio, counter);
Similar to ltcmelo's example, but if the objects themselves don't contain the counter:
struct sort_with_counter {
sort_with_counter(const double d): counter(d) {}
bool operator()(const MyStruct* a, const MyStruct* b) {
return(counter*a->arrival < counter*b->arrival);
}
const double counter;
};
mylist.sort(sort_with_counter(5.0));
If your counter is an external variable like that though it won't affect the ordering (at least if it's positive - thanks onebyone!) - so this may in fact not be necessary at all (or maybe I misunderstand what you're after?). It's a useful technique in other cases though.
Create a functor, and store the extra value in the functor object:
struct CompareByCounter {
CompareByCounter(int c) : counter(c) {}
bool operator()(const MyStruct *lhs, const MyStruct *rhs) {
return (counter * lhs->arrival) < (counter * rhs->arrival);
}
private:
int counter;
};
// sort ascending
bob.sort(CompareByCounter(1));
// sort descending
bob.sort(CompareByCounter(-1));
Just create an function-object, a class/struct with an overload of operator() that does the right thing for you. In this case, taking into consideration the extra variables. Then, you pass an instance of it to the sort method.
struct my_comparison : binary_function<MyStruct const*, MyStruct const*, bool>
{
bool operator()(MyStruct const* a, MyStruct const* b)
{
return (a->counter * a->arrival) < (b->counter * b->arrival);
}
};
//Use it this way.
my_comparison comp;
//Set the arrival and counter data in instance comp.
/* ... */
//Now, pass it to the list.
bob.sort(comp);
EDIT: I just noticed that you have a list of pointers so I changed a bit the struct.