Difference in behaviour when using a map comparator - c++

When declaring a std::map with a custom class is there any difference if I use a comparator as opposed to just implementing the less than operator for the class? Is one 'wrong' or considered bad?
Any difference between this:
std::map <MyClass, float, CompareMyClass> myMap2;
And this:
std::map <MyClass, float> myMap; // where MyClass has the less than operator implemented
Full Code:
class MyClass
{
public:
bool operator()(const MyClass& a, const MyClass& b) const
{
return a.value_ < b.value_;
}
bool operator<(const MyClass& myClass) const
{
return value_ < myClass.value_;
}
private:
friend struct CompareMyClass;
float value_;
};
struct CompareMyClass
{
bool operator()(const MyClass& a, const MyClass& b) const
{
return a.value_ < b.value_;
}
};
int main(int argv, char** args)
{
std::map <MyClass, float> myMap;
MyClass a;
myMap[a] = 1.99;
std::map <MyClass, float, CompareMyClass> myMap2;
MyClass b;
myMap[b] = 1.99;
system("PAUSE");
return 0;
}

If you provide an operator<, its behaviour should not be surprising. Now, for different uses, you might want to sort a record by different criteria, and for such a record, any operator< would always have surprising behaviour. In such a case, it is better to create a comparator who's name already gives a hint how it sorts, because it makes it explicit how a map (or other things) are sorted.

Related

Sorting list of shared pointers

Given the class
class objects {
public:
bool compareArea (const objects& obj) const { return this->area < obj.area; }
private:
double area;
};
I want to sort a
list<shared_ptr<objects>> myObjects;
I cannot use a lambda (since my toolchain's C++11 support is incomplete). Thus, I tried the following:
using namespace placeholders;
myObjects.sort(bind(&objects::compareArea,_1,_2));
This line is called from another file (not from a class member!). The problem is, that compareArea requires two objects as input. But I give two shared pointer to objects to it. Is there an easy way of how to include the dereferencing of the pointers into the sort-call? I want the objects::compareArea(..) function to stay as it is. I do not want this kind of solution
bool compareArea (const shared_ptr<objects>& ptr1, const shared_ptr<objects>& ptr2) {
return ptr1->area > ptr2->area;
}
// in same source-file:
myObjects.sort(bind(compareArea,_1,_2));
where compareArea is no member-function of objects. Actually an operator overloading of < would be my favourite solution.
I would strongly suggest that you never store any kind of pointer in a container.
Instead, make a handle class which supports the required arithmetic and comparison operators.
It makes for code that's easier to reason about:
class objects {
public:
objects(double w, double h) : area(w * h) {}
bool operator<(const objects& r) const { return this->area < r.area; }
private:
double area;
};
struct object_handle
{
object_handle(shared_ptr<objects> const& ptr) : ptr_(ptr) {}
static object_handle create(double w, double h) { return make_shared<objects>(w,h); }
bool operator < (object_handle const& r) const {
return *ptr_ < *r.ptr_;
}
shared_ptr<objects> ptr_;
};
int main() {
std::vector<object_handle> mylist;
mylist.push_back(object_handle::create(10, 7));
mylist.push_back(object_handle::create(2, 5));
std::sort(mylist.begin(), mylist.end());
}
Lambdas are just syntactic sugar for a class with operator(), so you can very easily write one directly (especially if you don't need captures):
struct Comparator
{
bool operator() (const shared_ptr<objects> &lhs, const shared_ptr<objects> &rhs) const
{
return lhs->compareArea(*rhs);
}
};
myObjects.sort(Comparator());

Use member functions directly with unordered_set

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();
}

Custom comparison operator and custom class for std::set in C++

I would like to create a set containing the objects of my class, I have to determine a custom comparison. Unfortunately, everything I tried did not work.
class My_Class {
public:
char letter;
set<My_Class, compare> Children;
};
Ant then, the compare struct:
struct compare {
bool operator() (const My_Class& a, const My_Class& b) const{
return a.letter < b.letter;
}
};
How can I make this work please?
Currently, the issue displays that identifiers a and b are not declared.
You are trying to use compare structure inside My_Class, which uses My_Class in its method. It is not a trivial case, but forward declaration will help. So this should work:
class My_Class;
struct compare {
bool operator() (const My_Class &a, const My_Class &b) const;
};
class My_Class {
public:
char letter;
set<My_Class, compare> Children;
};
bool compare::operator() (const My_Class &a, const My_Class &b) const
{
return a.letter < b.letter;
}
Another alternative would be to pass comparator to std::set constructor, rather than specify it as a template parameter:
class My_Class {
public:
My_Class();
char letter;
set<My_Class> Children;
};
struct compare {
bool operator() (const My_Class& a, const My_Class& b) const{
return a.letter < b.letter;
}
};
My_Class::My_Class() : Children( compare() )
{
}
The problem with your code is that it is not guaranteed to compile. The problem is not the compare struct, so take that out of the picture. It is this:
class My_Class {
public:
char letter;
set<My_Class, compare> Children; // it is the set<My_Class> that is the problem
};
You are defining a std::set of My_Class before the definition of My_Class is known to the compiler. In other words, you're using an incomplete type within the std::set container. There is no guarantee that the code will compile, and even if it did, the behavior now is undefined.
If you want a container that works with incomplete types, you can use the Boost container types here:
http://www.boost.org/doc/libs/1_55_0/doc/html/container.html
Here is the description about Incomplete Types within the Boost documentation:
http://www.boost.org/doc/libs/1_55_0/doc/html/container/main_features.html#container.main_features.containers_of_incomplete_types

std::equal_range not working with strucutre having operator< defined

I am trying to use std::equal_range with the structure below I have compilation error saying that error: no match for ‘operator<’ .
struct MyFoo {
int v_;
string n_;
bool operator<(int v) const
{ return v_ < v;}
};
vector<MyFoo> data;
// data is sorted by int v_
typedef vector<MyFoo>::iterator Ptr;
std::pair< Ptr, Ptr > pr = std::equal_range(data.begin(), data.end(), 10);
I've looked into the template implementatino and what is failing is the following where *it is deferenging the iterator pointing to an object of MyFoo and val_ is 10.
if(*it < val_) {
...
}
Why it is not working? I thought probably because it is trying to call the the global operator< that is not defined, but since I defined it as class member that should not be a problem, isn't it?
Provide non-member comparison operators :
bool operator<(int v, const MyFoo& foo)
{
return foo.v_ < v;
}
bool operator<(const MyFoo& foo, int v)
{
return v < foo;
}
Alternatively, you can provide a conversion operator to int :
operator int() cont {return v_;}
Which is probably unwanted, since the compiler will be able to perform silent conversions in other places of your code.
As an other alternative: provide
bool operator<(const MyFoo& rhs) const { return v_ < rhs.v_; }
And use std::equal_range on a dummy object with correct v_ as:
std::pair<Ptr, Ptr> pr = std::equal_range(data.begin(), data.end(), MyFoo{10, ""});
You may be having trouble because the std::equal_range implementation uses std::less. This is going to try to convert your MyFoo to an int to do the comparison, rather than just using an operator<() overload. Try adding this to your MyFoo class...
operator int() const
{
return v_;
}

Declaring function objects for comparison?

I have seen other people questions but found none that applied to what I'm trying to achieve here.
I'm trying to sort Entities via my EntityManager class using std::sort and a std::vector<Entity *>
/*Entity.h*/
class Entity
{
public:
float x,y;
};
struct compareByX{
bool operator()(const GameEntity &a, const GameEntity &b)
{
return (a.x < b.x);
}
};
/*Class EntityManager that uses Entitiy*/
typedef std::vector<Entity *> ENTITY_VECTOR; //Entity reference vector
class EntityManager: public Entity
{
private:
ENTITY_VECTOR managedEntities;
public:
void sortEntitiesX();
};
void EntityManager::sortEntitiesX()
{
/*perform sorting of the entitiesList by their X value*/
compareByX comparer;
std::sort(entityList.begin(), entityList.end(), comparer);
}
I'm getting a dozen of errors like
: error: no match for call to '(compareByX) (GameEntity* const&, GameEntity* const&)'
: note: candidates are: bool compareByX::operator()(const GameEntity&, const GameEntity&)
I'm not sure but ENTITY_VECTOR is std::vector<Entity *> , and I don't know if that could be the problem when using the compareByX function object ?
I'm pretty new to C++, so any kind of help is welcome.
And a third one comes in... After you edited you question, still one open topic: your comparator takes a const & to the GameEntity class. It should, in order to work with the values of the vector<GameEntity*>, take const GameEntity* arguments instead.
A functor is a class that defines operator() so an object of that class can be "invoked" with the same syntax as calling a function:
struct functor {
bool operator()(Entity const &a, Entity const &b) {
return a.x < b.x;
}
};
If you want that as a member of your Entity class, you'd use a nested class:
class Entity {
float x;
public:
friend class byX;
class byX {
bool operator()(Entity const &a, Entity const &b) {
return a.x < b.x;
}
};
};
Then your sort would look something like this:
std::sort(ManagedEndities.begin(), ManagedEntities.end(), Entity::byX());
Alternatively, if you usually sort Entities by X, you could define operator< for Entity:
class Entity {
float x;
public:
bool operator<(Entity const &other) {
return x < other.x;
}
};
In this case, your use of sort would be a bit simpler:
std::sort(ManagedEntities.begin(), ManagedEntities.end());
Creating the comparison function as a normal member function of the Entity class, however, will lead to a sort invocation that's pretty ugly -- it'll usually need something like std::mem_fun_ref to do the job; it's sufficiently ugly that I'd generally avoid it for real code.
I did see this question, recently, though....
The answer was something in the way of: the function provided to sort should not be a member-function of something. Meaning: it should be a static function, or a free function. In case you declare it a static function, you should still precede it by Entity::compareByX in order to name it correctly.
If you define the order in the class itself, you can, as aJ already said, use a function adapter mem_fun or mem_fun_ref to pour it into a 'free' functor object.
If you want an Entity object to do the comparison, you should provide sort with an object (called a functor or comparator in this case):
struct EntityComp {
bool operator()( const GameEntity& a, const GameEntity& b ) const {
return a.x < b.x;
}
}
...
std::sort( v.begin(), v.end(), EntityComp() );
I believe compareByX should be a static member or lake a look here
In the light of 'what you're trying to achieve', I may do another guess... You want to be able to specify whether to compare your objects by their GameEntity::x member, or by their GameEntity::y member.
The easiest way would be to, as you did, specify a functor for each member:
struct CompareX {
bool operator()( const GameEntity& a, const GameEntity& b ) const {
return a.x < b.x;
}
};
struct CompareY {
bool operator()( const GameEntity& a, const GameEntity& b ) const {
return a.y < b.y;
}
};
CompareX compx; // create a compare object
std::sort( v.begin(), v.end(), compx );
The 'flexible' yet more cumbersome way would be to create a template functor:
#include <iostream>
using namespace std;
// a mockup of your class
struct GameEntity { float x, y, z; };
// just to be able to print it...
ostream& operator<<( ostream& o, const GameEntity& g ) {
return o << "(" << g.x << ", " << g.y << ", " << g.z << ")";
}
// cumbersome starts here...
typedef float (GameEntity::*membervar);
// a 'generic' float-member comparator
template< membervar m > struct CompareBy {
bool operator()( const GameEntity& a, const GameEntity& b ) const {
return a.*m < b.*m ;
}
};
// example code
int main() {
using namespace std;
GameEntity v[] = { {1,0,0}, {2,0,1}, {3,-1,2} };
GameEntity* vend = v + sizeof(v)/sizeof(v[0]);
sort( v, vend, CompareBy< &GameEntity::x >() );
copy( v, vend, ostream_iterator<GameEntity>( cout, "\n" ) );
}
try this..
class CompareByX
{
operator ()(const GameEntity &a, const GameEntity &b) { ... };
};
...
std::sort( this->begin(), this->end(), CompareByX);
In a nutshell, a functor is a function object - the STL looks specifically for an operator () that takes in the two parameters I've specified. If you're new to C++, I suggest you look up operators and functors - they're pretty handy even outside STL.
Edit: Jerry's answer is better, and more comprehensive.