std::list::merge() fails for lists containing objects - c++

I have following situation:
#include <list>
struct Example
{
double p1;
double p2;
};
void f()
{
std::list<Example> list1;
std::list<Example> list2;
list1.merge(list2);
}
During build I get errors:
C2672 'operator __surrogate_func': no matching overloaded function found
C2893 Failed to specialize function template 'unknown-type std::less<void>::operator() (_Ty1 &&,_Ty2&&) const'
If I comment the last line of code, the build is successful. I find it hard to believe that lists that contain objects cannot be merged, so: what am I missing?
PS. I use Visual Studio Community 2015

As stated in documentation std::list::merge:
Merges two sorted lists into one.
And further:
The first version uses operator< to compare the elements
So you either need to provide operator< for your structure or use overloaded version with custom comparator. For example standalone function could be:
bool operator<( const Example &e1, const Example &e2 ) {
return std::tie( e1.p1, e1.p2 ) < std::tie( e2.p1, e2.p2 );
}

A possible way out of the problem is to define a meaningful relational operator< that defines order of your class Example objects. This could be done by:
An overloaded operator< withing the class, which will be implicitly used.
Lambda expression directly inserted as a second parameter in the merge() function.
Separate comparison class, whose object instantiation could be used as second parameter in merge()
For example, you could do something like:
bool operator< (const Example& lhs, const Example& rhs) const
{
return lhs.p1 < rhs.p1; // if p1 is used as a criterion
}

give std::list::merge a way to compare
class Examples{...};
for example, you can do this by passing in a lambda
void f()
{
std::list<Example> list1;
std::list<Example> list2;
list1.merge(list2,[](Example e1,Example e2)
{
return (e1.p1==e2.p1)? (e1.p2 < e2.p2) : (e1.p1 < e2.p1);
});
}
or you can just do
using Example = std::pair<double,double>;
and then std::list::merge will already know how to compare these (assuming you want the default comparison rules for std::pair)

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.

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.

Creating Min Heap from STL Priority Queue

I am creating a min heap from stl priority queue. Here is my class which I am using.
class Plane
{
private :
int id ;
int fuel ;
public:
Plane():id(0), fuel(0){}
Plane(const int _id, const int _fuel):id(_id), fuel(_fuel) {}
bool operator > (const Plane &obj)
{
return ( this->fuel > obj.fuel ? true : false ) ;
}
} ;
In main I instantiate an object thus.
priority_queue<Plane*, vector<Plane*>, Plane> pq1 ;
pq1.push(new Plane(0, 0)) ;
I am getting an error from xutility which I am unable to understand.
d:\microsoft visual studio 10.0\vc\include\xutility(674): error C2064: term does not evaluate to a function taking 2 arguments
Any help to its solution would be appreciated.
If you drop the use of pointers (which are overkill for your simple structures), then you can use std::greater from the header functional:
std::priority_queue<Plane, std::vector<Plane>, std::greater<Plane> > pq1;
pq1.push(Plane(0, 0));
Currently, you are feeding Plane as the comparison type. That won't work since the comparison type must be a type of function object, i.e. it must have an operator() that does the comparison. Plane doesn't have such a member (and adding it just for this purpose would be a bad idea).
std::greater has the appropriate method, implemented in terms of your operator>. However, it doesn't work with pointers, because then it uses a pointer comparison (based on memory addresses).
Btw., note that your comparison function can be expressed more succinctly as
bool operator>(const Plane &other)
{
return fuel > other.fuel;
}
The third template parameter must be a binary functor taking teo Plane*. Your Plane class does not provide that.
You need something of the form
struct CompPlanePtrs
{
bool operator()(const Plane* lhs, const Plane* rhs) const {
return lhs->fuel > rhs->fuel ;
}
};

Compilation error on operator overloading when sorting vector

I have a class roughly defined like below. Among others, it has a < comparison operator.
class DictionarySearchItem {
public:
DictionarySearchItem();
double relevance() const;
bool operator<(const DictionarySearchItem& item) { return relevance() > item.relevance(); }
};
typedef std::vector<DictionarySearchItem> DictionarySearchItemVector;
I then use the class this way:
DictionarySearchItemVector searchItems;
for (unsigned i = 0; i < entries.size(); i++) {
// ...
// ...
DictionarySearchItem item;
searchItems.push_back(item);
}
However when I try to sort the vector:
std::sort(searchItems.begin(), searchItems.end());
I'm getting the following compilation error with MinGW.
/usr/include/c++/4.2.1/bits/stl_algo.h:91: erreur : passing 'const hanzi::DictionarySearchItem' as 'this' argument of 'bool hanzi::DictionarySearchItem::operator<(const hanzi::DictionarySearchItem&)' discards qualifiers
I don't quite understand what is incorrect with my code and the error message is not clear to me. The same code compiles fine with MSVC2008. Any idea what could be the issue?
You need to make the less-than operator const:
bool operator<(const DictionarySearchItem& item) const { ... }
^
The reason is probably that sort relies on the fact that elements being compared cannot change as a result of the comparison. This can be enforced by having both sides of the < comparison be const, which means the operator has to be const, as well as it's argument.

How can i make a function that finds an object in a set?

I'm making a program that generates a maze and then uses bredth first search to find a way in the maze. My function that checks if an element is present in a container-class now uses the vector like this (where coordinatePath is a typedef for vector) :
bool Labyrinth::inVisited(const Coordinate &c, const coordinatePath &visited ) const
{
for each (Coordinate coord in visited)
{
if(coord == c)
return true;
}
return false;
}
Since this method has to traverse the full container if an element is not present it's very ineffective for large searches. I tried to implement the same function that uses a set instead of a vector and wrote it like this:
bool Labyrinth::inVisited(const Coordinate &c, const set<Coordinate> &visited ) const
{
return (visited.find(c) != visited.end());
}
when i try to recomplie i get a lot of errors where the topmost is
Error 22 error C2676: binary '<' : 'const Coordinate' does not define this operator or a conversion to a type acceptable to the predefined operator c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstddef 193
I don't really understand these particular debug-messages and wonder if there is a way to implement this faster search!
In order to create a std::set of objects, those objects have to define operator <.
So you need to add the following operator:
inline bool operator < (const Coordinate& first, const Coordinate& other);
To use elements in a set the value_type has to define operator< or you need to provide a comparison functor to the container. Apparently, your Coordinate type doesn't do that or the operator< you provide takes incompatible arguments. It should look roughly like this:
struct Coordinate {
bool operator<(const Coordinate& other) const { return false; }
};
// or by providing a functor
struct CmpCoord {
bool operator()(const Coordinate& x, const Coordinate& y);
};
typedef std::set<Coordinate, CmpCoord> coord_set;