How to write a comparison operator in c++? - 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());

Related

Custom arguments to std::set comparison function

I know how to pass a regular comparison class or function to set::set<>.
I am writing some test code and I want to emulate some C libraries using STL's std::set and I want to be able to pass a C callback to the comparison object so a different comparison takes place.
I have the following theoretical code:
struct MyClass
{
int a;
};
typedef bool (*user_callback_t)(void *, void *);
class MyComparison
{
private:
user_callback_t cb = nullptr;
public:
MyComparison(user_callback_t cb): cb(cb) { }
MyComparison() {}
bool operator()(const MyClass &a, const MyClass &b) const
{
return cb((void *)&a, (void *)&b);
}
};
int f1()
{
auto cmp = [](void *a, void *b) -> bool
{
return *(int *)a < *(int *)b;
};
MyComparison mycmp(cmp);
std::set<MyClass, MyComparison> m1;
m1.insert({ 1 });
m1.insert({ 2 });
m1.insert({ 3 });
return 0;
};
Now notice how I can do:
std::set<MyClass, MyComparison> m1;
But I cannot, somehow, instantiate a MyComparison object, pass it "cmp" and then use that initialize comparison object with that specific set.
Is there a way to achieve that?
Your assertion that you can't pass a MyComparison instance to std::set for it to use it is wrong. There is a constructor for std::set expecting just that (in C++11):
explicit set( const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
So your MyComparison can be passed as first argument.
std::set<MyClass, MyComparison> m1(mycmp);
If you don't have C++11 or newer available, then this constructor overload is non-existing and you need to use another one:
template< class InputIt >
set( InputIt first, InputIt last,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
This one however expects an interator range in the first two arguments. Since we don't actually want to construct from a range, they need to be dummies. You could do someting like:
std::vector<int> dummy;
std::set<MyClass, MyComparison> m1(dummy.begin(), dummy.end(), mycomp);
dummy will not be used afterwards. I am not sure whether there is a nicer solution besides implementing a dummy iterator class.
See http://en.cppreference.com/w/cpp/container/set/set for full reference on constructors of std::set.

Comparing struct in 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.

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>.

Error in operator function in multimap

I am trying to create a multimap using multikey structure as a key and I am getting a error described below:
code:
struct stCont
{
long long Tok;
char Reserved;
long long Asset;
}
struct MultiKey {
char InstrumentName[6];
char Symbol[10];
long long ExpiryDate;
}
struct myComp
{
bool operator() (const MultiKey& lhs, const MultiKey& rhs)
{
if((lhs.ExpiryDate==rhs.ExpiryDate)&&(memcmp(lhs.InstrumentName,rhs.InstrumentName,6))&&(memcmp(lhs.Symbol,rhs.Symbol,10)))
{
return 1;
}
return 0;
}
};
std::multimap<MultiKey, stCont,myComp> cont_map;
error:
expression having type 'const myComp' would lose some const-volatile qualifiers in order to call 'bool myComp::operator ()(const MultiKey &,const MultiKey &)'
you should rewrite the multimap code like this and remove the mycomp structure:
struct MultiKey {
char InstrumentName[6];
char Symbol[10];
long long ExpiryDate;
bool operator< (const MultiKey& lhs)const
{
if((lhs.ExpiryDate==ExpiryDate)&&(memcmp(lhs.InstrumentName,InstrumentName,6))&&(memcmp(lhs.Symbol,Symbol,10)))
{
return true;
}
return false;
}
};
Why don't you just write just write operator < for MultiKey? Or you'll have to change myComp because it isn't what multimap wants anyway (it wants a less-than comparison).
Look at the The C++11 Standard, §23.4.5.1 and the header:
template <class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T> > >
class multimap {
public:
// ...
class value_compare {
friend class multimap;
protected:
Compare comp;
value_compare(Compare c) : comp(c) { }
public:
typedef bool result_type;
typedef value_type first_argument_type;
typedef value_type second_argument_type;
bool operator()(const value_type& x, const value_type& y) const {
return comp(x.first, y.first);
}
};
// ...
};
The comparison function as defined by class value_compare is const. Now, I may be misinterpreting the standard, but this definition seems to be invalid if the operator() is non-const in class Compare.
As to why does it work for some people... Perhaps some finer points about instantiation rules prevent this from being an error, or the implementations are not requierd to adhere strictly to the type definitions in the standard; if so, I'd be glad if someone more versed with The Standard could clarify.
To fix the compile error, declare the comparison function to be a const member:
bool operator() (const MultiKey& lhs, const MultiKey& rhs) const
^^^^^
Then you have another problem: the comparator needs to perform a "less than" comparison, but yours does an equality comparison. You want something like
if (lhs.ExpiryDate < rhs.ExpiryDate) return true;
if (lhs.ExpiryDate > rhs.ExpiryDate) return false;
if (memcmp(lhs.InstrumentName,rhs.InstrumentName,6) < 0) return true;
if (memcmp(lhs.InstrumentName,rhs.InstrumentName,6) > 0) return false;
if (memcmp(lhs.SymbolName,rhs.SymbolName,10) < 0) return true;
return false;
You may find it more convenient to overload operator< rather than defining a named comparator type, so that you can use std::multimap<MultiKey, stCont> with the default comparator.

Sort a std::list<myclass*> with myclass::operator<(myclass &other)

I have a std::list<myclass*> and in my class I have myclass::operator<(myclass &other) defined.
I use the std::list.sort() function, but it does not change anything in that list. Maybe it just sorts the pointers?
How can I sort the actual items in that list?
You are sorting the pointer values, not the myclass values. You have to write your own predicate to compare pointers by dereference:
template <typename T> bool PComp(const T * const & a, const T * const & b)
{
return *a < *b;
}
std::vector<Foo*> myvec;
std::list<Foo*> mylist;
std::sort(myvec.begin(), myvec.end(), PComp<Foo>);
mylist.sort(PComp<Foo>);
By the way, I think you cannot sort std::list with std::sort from <algorithm> because it is not random access. Use the member function sort instead as MerickOWA says. (But that's generally less efficient than sorting a random-access container.) Alternatively, you can immediately store your objects in a sorted container like std::set<Foo*, PPred>, where PPred is the functor version of the predicate:
struct PPred {
template <typename T> inline bool operator()(const T * a, const T * b) const
{ return *a < *b; }
};
Several answers propose using a predicate that explicitly takes two pointers; this will work for your current case where you have a container of raw pointers, but it won't work for any other dereferenceable type, like smart pointers or iterators.
Why not go the more general route and match any type?
struct indirect_compare
{
template <typename T>
bool operator()(const T& lhs, const T& rhs) const
{
return *lhs < *rhs;
}
}
While a const reference is unnecessary for a T*, it is necessary for smart pointer types that are relatively expensive to copy (e.g. std::shared_ptr) or impossible to copy (e.g. std::unique_ptr).
Alternatively, you might consider using something like Boost's indirect_iterator, which moves the indirection into the iterator and can make for much cleaner code.
It'll sort the pointer as std::sort( Container ) use the operator< defined T. Here T is myclass*, then it is sorted using comparison over pointer.
You can pass a custom comparator functor to std::sort so make one take takes two myclass* and return the proper comparison :
template<class T>
struct ptr_comparison
{
bool operator()(T* a, T* b) { return *a < *b; }
};
list<myclass*> mylist;
// later
mylist.sort(ptr_comparison<myclass>());
assuming you don't have NULL pointers in your list just do
bool ptrsorter( myclass *a, myclass *b ) {
return *a < *b;
}
mylist.sort( ptrsorter );
or if you're lucky enough to be using a more recent compiler (with C++0x-support), you can use a lambda-expression:
mylist.sort( []( myclass *a, myclass *b ) { return *a < *b } );
I was trying out the template PComp example and since other classes would be using the PComp I stuck in the the include file for the class the std::list was holding. However when I compiled the task I got this:
In member function ‘EcTVoid FdFtTSBFDFTableLoadAppl::IngestTableProducts(const string&, const EcTInt&)’:
/mms_builds/working/jkerich/MMS_SDF/sw/mms/dms/FdFtTSB/src/FdFtTSBFDFTableLoadAppl.C:782:53 error: no matching function for call to ‘std::list<FoFmVirtualTbl*>::sort(<unresolved overloaded function type>)’
myIngestedTables.sort(sorter.PComp<FoFmVirtualTbl>); // sorted list
^
/mms_builds/working/jkerich/MMS_SDF/sw/mms/dms/FdFtTSB/src/FdFtTSBFDFTableLoadAppl.C:782:53 note: candidates are:
In file included from /usr/include/c++/4.8.2/list:64:0,
from /mms_builds/working/jkerich/MMS_SDF/sw/mms/foscommon/common/FoCf/include/FoCfConfigSetup.h:11,
from /mms_builds/working/jkerich/MMS_SDF/sw/mms/foscommon/common/FoCf/include/FoCfTypes.h:156,
from /mms_builds/working/jkerich/MMS_SDF/sw/mms/dms/FdFtTSB/src/FdFtTSBFDFTableLoadAppl.C:18:
/usr/include/c++/4.8.2/bits/list.tcc:353:5: note: void std::list<_Tp, _Alloc>::sort() [with _Tp = FoFmVirtualTbl*; _Alloc = std::allocator<FoFmVirtualTbl*>]
list<_Tp, _Alloc>::
^
/usr/include/c++/4.8.2/bits/list.tcc:353:5: note: candidate expects 0 arguments, 1 provided
/usr/include/c++/4.8.2/bits/list.tcc:430:7: note: void std::list<_Tp, _Alloc>::sort(_StrictWeakOrdering) [with _StrictWeakOrdering = bool (FoFmVirtualTbl::*)(const FoFmVirtualTbl* const&, const FoFmVirtualTbl* const&); _Tp = FoFmVirtualTbl*; _Alloc = std::allocator<FoFmVirtualTbl*>]
list<_Tp, _Alloc>::
^
/usr/include/c++/4.8.2/bits/list.tcc:430:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘bool (FoFmVirtualTbl::*)(const FoFmVirtualTbl* const&, const FoFmVirtualTbl* const&)’
In file included from /ecs/MMS_SDF/RogueWave/13.2/dbg/rw/config/rwconfig_tls.h:9:0,
from /ecs/MMS_SDF/RogueWave/13.2/dbg/rw/compiler.h:5,
from /ecs/MMS_SDF/RogueWave/13.2/dbg/rw/defs.h:46,
from /ecs/MMS_SDF/RogueWave/13.2/dbg/rw/cstring.h:33,
from /mms_builds/working/jkerich/MMS_SDF/sw/mms/foscommon/common/FoCf/include/FoCfTypes.h:11,
from /mms_builds/working/jkerich/MMS_SDF/sw/mms/dms/FdFtTSB/src/FdFtTSBFDFTableLoadAppl.C:18:
The template is this:
template <typename T> bool PComp(const T * const & a, const T * const & b)
{
EcTInt status = a->compareTo(b);
return ((status == -1) ? true : false);
}
the calls I tried in FdFtTSBFDFTableLoadAppl.C were:
myIngestedTables.sort(PComp<FoFmVirtualTbl>); // sorted list
or
FoFmVirtualTbl sorter;
myIngestedTables.sort(sorter.PComp<FoFmVirtualTbl>); // sorted lis
t
Only when the template was declared in FdFtTSBFDFTableLoadAppl did it compile ok. So how would I get this to compile if the template is in some others class include?