Comparing struct in c++ - c++

Do anyone know a general method to declare a comparision function for struct so that I can use it in sort , priority queue , map ,set ...
I would also know how to specify the comparision function when declaring a data structure (like map ) having a structure as a key (in the case where i have two or more comparision functions)
Thank you in advance

How can the method be "general"?
Let's say you have this struct.
struct MyStruct{
A a; // A is your own class
};
How would the compiler know how to compare objects of type A?
You need to define a comparison operator yourself.
bool operator()(const MyStruct& s1, const MyStruct& s2);
This function can be given as a compare-function when creating for example a std::map.
explicit map (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
std::map
comp: Binary predicate that, taking two element keys as argument, returns true if the first argument goes before the second argument in the strict weak ordering it defines, and false otherwise.
defaults to
less<key_type>

The comparison function depends from the semantics of your struct. What does it mean that a < b for your type?
In general, a compare function is something along the line of this (references are optional):
bool comp( const YourType& a, const YourType& b );
To make a map use your compare function, you must write like this:
#include <map>
struct YourType{
int v;
};
struct YourTypeComparison{
bool operator()( const YourType& a, const YourType& b ) { return a.v < b.v; }
};
int main()
{
std::map<YourType,int, YourTypeComparison> m;
}

Normally you would use the standard containers like std::map< std::string, int >. But they also have a Comparator type and an Allocator type.
The Comparator used by default is std::less, which looks somewhat like this,
template <class T>
struct less : binary_function <T,T,bool> {
bool operator() (const T& x, const T& y) const {
return x<y;
}
};
(There are some other already made functors http://en.cppreference.com/w/cpp/utility/functional)
Notice that it compares two objects with <. This means that as a "general method" you only need to implement the operator bool operator< (const X& lhs, const X& rhs){...} to allow your objects to be sorted. See Operator Overloading FAQ. As a rule of thumb, if you're going to implement one comparison operator then you should implement the others too.
If you need to sort your keys in another way you can define your own comparator (functor).
template < class T >
struct myLess {
bool operator()( const T& lhs, const T& rhs ) const {
return lhs < rhs;
}
};
And use it in a map like std::map< int, int, myLess<int> >.
You can also not use templates at all if you only need to compare one type.
struct myLess {
bool operator()( const int& lhs, const int& rhs ) const {
return lhs < rhs;
}
};
Then you only have to write std::map< int, int, myLess >.
Keep in mind that the objects you're comparing are the Key types, not necessarily the Contained types.

Related

user defined data type in set in c++

In the first case, code is working fine but I am getting an error in the second code, the only difference is of "const" in the operator overloading of '<', I am not able to figure out why.
Code for both cases are below
1.
class first
{
public:
int y;
bool operator < (first t) const
{
return (y>t.y);
}
};
set<first> f;
2.
class first
{
public:
int y;
bool operator < (first t)
{
return (y>t.y);
}
};
set<first> f;
The default comparator for std::set is std::less<Key> and as we can see on std::less it defines:
constexpr bool operator()( const T& lhs, const T& rhs ) const;
Which accepts const arguments and returns lhs < rhs. This of course doesn't work if lhs < rhs is not valid for const arguments, as it is in your second case because bool operator < (first t) cannot be called.
Writing your own comparator which accepts non-const arguments doesn't seem to work either, so it looks it is a requirement of the ordered container, but further check with the standard is needed to confirm.
std::set, like most other containers in the standard library, uses the requirement Compare for its comparing function. And the Compare requirement enforces constness:
As with any BinaryPredicate, evaluation of that expression is not allowed to call non-const functions through the dereferenced iterators.

Modify std::less on a shared_ptr

This is what I have:
struct Foo {
int index;
}
std::set<std::shared_ptr<Foo>> bar;
I want to order bar's elements by their indices instead of by the default std::less<std::shared_ptr<T>> function, which relates the pointers.
I read I can type std::set<std::shared_ptr<Foo>, std::owner_less<std::shared_ptr<Foo>>> bar, but I'd prefer to stick to the previous syntax.
I tried defining std::less<std::shared_ptr<Foo>>, but it's not actually being used by the set functions. Is there a way I can achieve this?
If you want to compare by their indices, you'll have to write a comparator that checks by their indices. std::less<> will do the wrong thing (since it won't know about index) and std::owner_less<> will do the wrong thing (since it still won't compare the Foos, but rather has to do with ownership semantics of them).
You have to write:
struct SharedFooComparator {
bool operator()(const std::shared_ptr<Foo>& lhs,
const std::shared_ptr<Foo>& rhs) const
{
return lhs->index < rhs->index;
}
};
and use it:
std::set<std::shared_ptr<Foo>, SharedFooComparator> bar;
You could additionally generalize this to a generic comparator for shared_ptr's:
struct SharedComparator {
template <typename T>
bool operator()(const std::shared_ptr<T>& lhs,
const std::shared_ptr<T>& rhs) const
{
return (*lhs) < (*rhs);
}
};
and then simply make Foo comparable.
You can provide your own specialization of less<shared_ptr<Foo>> in the std namespace.
namespace std
{
template<>
class less<shared_ptr<Foo>>
{
public:
bool operator()(const shared_ptr<Event>& a, const shared_ptr<Event>& b)
{
// Compare *a and *b in some way
}
};
}
Then you can form a set<shared_ptr<Foo>> without a comparator. I needed this for a priority_queue<shared_ptr<Foo>>, where I didn't want to use a priority_queue<Foo*, vector<Foo*>, int (*)(const Foo*, const Foo*)>. I am not proud of it, but it works.

c++ set and shared_ptr

I have class X like:
class X {
public:
bool operator<(const SCN& other) const;
};
Then I have the following code:
std::multiset<std::shared_ptr<X>> m;
My questions are:
how the data in m is ordered? the address of X(shared_ptr) or the X.operator<? If it is ordered by address of X, how can I make it order by X.operator<?
for this m, if I want to access its elements from smallest to biggest, can the following code guarantee that? If not, How?
for (auto& i : m) {
f(i);
}
Your set is ordered based on your key_type which is std::shared_ptr<X>. As your std::shared_ptr<X> is comparable, the ordering of the std::shared_ptr prevails.
For the sake of reference, multiset is defined as
template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class multiset;
As can be seen, typename Compare is std::less<Key> and std::less should overload the function overload which would possibly be implemented as
constexpr bool operator()(const T &lhs, const T &rhs) const
{
return lhs < rhs;
}
both lhs and rhs is of type T which in this case is Key which is the type that we have instantiated multiset with which is std::shared_ptr<X>.

How to write a comparison operator in c++?

I'm having an array of structure containing three fields:
struct data{
int s;
int f;
int w;
};
struct data a[n];
In order to sort the array of structure based on field f I'm using my own comparison operator :
bool myf( struct data d1,const struct data d2){
return d1.f < d2.f ;
}
The above operator works fine in inbuilt sort() function :
sort(a,a+n,myf);
but it's not working for upper_bound() function :
upper_bound(a,a+n,someValue,myf);
Can anyone tell me where am I doing wrong ? Is my comparison operator wrong ? If it's wrong, why is it working for sort() function and not upper_bound() ?
I'm getting following on compilation :
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_algo.h: In function ‘_FIter std::upper_bound(_FIter, _FIter, const _Tp&, _Compare) [with _FIter = data*, _Tp = int, _Compare = bool (*)(data, data)]’:
prog.cpp:37: instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_algo.h:2243: error: conversion from ‘const int’ to non-scalar type ‘data’ requested
All you actually need here is to create operator< for your type:
inline bool operator<( const data& lhs, const data& rhs ) {
return lhs.f < rhs.f;
}
and standard algorithms will magically work for you.
In C++ you don't need struct when referring to a type like in C, and you want to pass by const reference to avoid copying.
Edit 0:
The above overloads standard comparison operator < for your type. You would use it implicitly as:
data values[N];
// ... populate
std::sort( values, values + N );
or explicitly with a standard functor:
std::sort( values, values + N, std::less<data>());
Edit 1:
See that compiler tells you _Tp = int in the warning? You need to pass an instance of data as third argument to upper_bound, not int:
data xxx = { 0, 1, 7 };
auto iter = std::upper_bound( values, values + N, xxx );
You can also create overloads for comparing to integers, like:
inline bool operator<( const data& lhs, int rhs ) {
return lhs.f < rhs;
}
inline bool operator<( int lhs, const data& rhs ) {
return lhs < rhs.f;
}
for your original invocation to work.
Primarily, it isn't working because the upper_bound overload that accepts a custom sorting takes four parameters:
// http://en.cppreference.com/w/cpp/algorithm/upper_bound
template< class ForwardIt, class T, class Compare >
ForwardIt upper_bound( ForwardIt first, ForwardIt last, const T& value,
Compare comp );
It was suggested in another answer that you introduce operator< for your type. However, do not do this just for the sake of one specific sorting. Only introduce comparison operators iff they actually make sense for your type.
If you don't follow this rule, future programmers might use your type and wonder about why something works that shouldn't, or vice versa. Your future evil twin may also want to use another sorting for his purpose.
E.g., it makes sense for a complex-datatype class, a SIMD-class (like std::valarray), but it doesn't make any specific sense for example for an Employee class:
Employee foo, bar;
if (bar > foo) {
// is bar taller than foo?
// is bar older than foo?
// is bar working better than foo?
// is bar bigger newbie than foo?
}
Instead, you could do it like this:
namespace employee_ordering {
struct by_name_ascending {
bool operator() (Employee const &lhs, Employee const &rhs) const {
return lhs.name() < rhs.name();
}
};
struct by_name_descending {
bool operator() (Employee const &lhs, Employee const &rhs) const {
return lhs.name() > rhs.name();
}
}
};
....
upper_bound(first, last, ..., employee_ordering::by_name_ascending());

Resolving operator ambiguity

I have the following map type...
std::map<D3DXCOLOR, ID3DXMesh*>
During compilation, xfunctional complains that it cannot resolve an ambiguity regarding the key type;
error C2593: 'operator <' is ambiguous
The candidate operators detected by the compiler are as follows;
built-in C++ operator<(DWORD, DWORD)
built-in C++ operator<(FLOAT, FLOAT)
built-in C++ operator<(D3DCOLORVALUE, D3DCOLORVALUE)
The D3DXCOLOR struct consists of 4 floats r, g, b, and a respectively but does not define a operator <. It does however provide cast functions for DWORD FLOAT and D3DCOLORVALUE, hence the entries in the candidate list.
I am contemplating the best way to resolve this problem. I could write my own inline operator for D3DXCOLOR, wrap the colour inside a new class which provides its own operator <, or is it possible to somehow hint to the compiler which implementation should be chosen from the list of candidates? The DWORD operator < would meet my requirements adequately.
You have three options. Supposing for example that you want them compared as colorvalues:
1) Define operator<:
bool operator<(const D3DXCOLOR &lhs, const D3DXCOLOR &rhs) {
return static_cast<D3DCOLORVALUE>(lhs) < static_cast<D3DCOLORVALUE>(rhs);
}
2) Specialize std::less:
namespace std {
template <>
struct less<D3DXCOLOR> {
bool operator()(const D3DXCOLOR &lhs, const D3DXCOLOR &rhs) {
return static_cast<D3DCOLORVALUE>(lhs) < static_cast<D3DCOLORVALUE>(rhs);
}
};
}
3) Supply a third template parameter to your map - note that this changes the type of the map, so if you pass the map around a lot this might be inconvenient. But it expresses that the ordering is to be used only for this map, not as the canonical "correct" order of colors for any other purpose.
struct mycomparator {
bool operator()(const D3DXCOLOR &lhs, const D3DXCOLOR &rhs) {
return static_cast<D3DCOLORVALUE>(lhs) < static_cast<D3DCOLORVALUE>(rhs);
}
};
std::map<D3DXCOLOR, ID3DXMesh*, mycomparator>
You can just pass a less-than functor to the map class template that should be used.
struct D3DXCOLOR_less {
bool operator ()(D3DXCOLOR const&a, D3DXCOLOR const& b) const { … }
};
std::map<D3DXCOLOR, ID3DXMesh*, D3DXCOLOR_less> foo;
This is definitely what I would do in this case, unless you also need the operator < for this class in other cases.
You'll need to write your own operator<, or provide a comparator functor to the map.
struct CompareColor {
bool operator()(D3DXCOLOR const & L, D3DXCOLOR const & R) const {
// Compare and return whether L is less than R
}
}
map<D3DXCOLOR, ID3DXMesh*, CompareColor> TheMap;
Define operator< function for D3DXCOLOR, as
bool operator<(const D3DXCOLOR &c1, const D3DXCOLOR &c2)
{
return <some boolean value>;
}
Or define a compare functor, something called D3DXCOLOR_LESS and pass it as third parameter to the std::map:
struct D3DXCOLOR_LESS
{
bool operator()(const D3DXCOLOR &c1, const D3DXCOLOR &c2)
{
return <some boolean value>;
}
};
std::map<D3DXCOLOR, ID3DXMesh*, D3DXCOLOR_LESS> colormap;
Actually RGBA color have no some default quasi-order like any scalar. And you shouldn't define one in global context, but you can define your own ordering and specify it in std::map template instance. See parameters description at http://www.sgi.com/tech/stl/Map.html