operator== not working in template function - c++

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.

Related

Overloading operator() for a new struct

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

std::list::merge() fails for lists containing objects

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)

Creating a map with custom class as key, from unordered map. However elements not useable due to const

I'm trying to to map elements from my unordered map that contain to a map that contain an Class T, int and sortBy being the compare class.
However I can't perform any functions on any elements in the map because they are all const. I have no idea how to make them not const, or why they were const in the first place.
unordered_map < int,LibrarySong> Library;
map < LibrarySong,int, LibrarySong:: sortbyPlay> sortedLibrary;
for(unordered_map<int,LibrarySong>:: iterator it = Library.begin(); it != Library.end(); ++it){
sortedLibrary.insert(pair <LibrarySong,int> ( it->second, it->first));
}
for(map < LibrarySong,int>::iterator it=sortedLibrary.begin(); it != sortedLibrary.end(); ++it){
cout << it->first.print() << endl; //Cant do anything because it is const
};
The LibrarySongs header has the struct
struct sortbyPlay {
bool operator() (const LibrarySong &lhs ,const LibrarySong &rhs) const{
return lhs.numPlay < rhs.numPlay;
}
};
I've tried removing the const in the function but that doesn't work either?
Maybe there is a better way to do this, I'm not sure. I was thinking of vectors and then using sort(), but I needed both values(probably could use a pair?) however I'm not sure if the const problem would still persist as well.
The const-ness of the map's comparator is completely irrelevant.
cout << it->first.print() << endl; //Cant do anything because it is const
The map's keys, the first values, are always const. As such, the print() method in the class must be a const method. Although your question completely fails to provide a minimal, complete, and verifiable example, it's obvious that your print() method must be a const method, and it is not.
A class method, such as print(), does not need to modify its object. At least not in a sane class design. Therefore, you should not have any issues declaring your print() method as a const class method.

std map find is always true

I have a problem using the std::map, specifically when using find.
I have the following code.
class MyClass
{
update(const QVariant&);
QVariant m_itemInfo;
std::map<QVariant, int> m_testMap;
}
void update(const QVariant& itemInfo)
{
if(m_itemInfo != itemInfo)
{
// The items are not equal
m_itemInfo = itemInfo;
}
if(m_testMap.find(itemInfo) == m_testMap.end())
{
// TestMap doesnt contain key itemInfo.
m_testMap.insert(std::make_pair(itemInfo, 1));
}
// More code
}
The function update is called several times (with different itemInfo objects) in my code. Now when I start to debug it, I see that the first time update is called, both the first and the second if loop are entered. So far so good. However the second time update is called I do see that the first if loop is called, but the second is skipped! What am I missing here?
I guess the problem that the first and second QVariants that you pass to your Update method have different type (for example, bool and uint). std::map::find doesn't use !=operator to compare keys, it uses operator < (less) by default. If two compared QVariant values have different types operators != and < may work contradictory.
std::map::find compares keys in the following way:
Two keys are considered equivalent if the container's comparison object returns false reflexively (i.e., no matter the order in which the elements are passed as arguments).
i.e. std::map::find considers that v1 is equal to v2
if(!(v1<v2) && !(v2>v1)) { //is TRUE !!!
}
To solve your problem, you should define a less comparison for std:map.
class QVariantLessCompare {
bool operator()(const QVariant& v1, QVariant& v2) const {
// ==== You SHOULD IMPLEMENT appropriate comparison here!!! ====
// Implementation will depend on type of QVariant values you use
//return v1 < v2;
}
};
And use QVariantCompare in a such way:
std::map<QVariant, int, QVariantLessCompare> m_testMap;
A more paradigmatic solution is to use QMap which correctly implements the comparison of most QVariant types. It won't do userTypes() out of the box, but this still might suit your application.
A cleaner version of the solution proposed by Володин Андрей, that builds, might look like:
struct QVariantLessCompare {
bool operator()(const QVariant& v1,const QVariant& v2) const
{
return v1.toInt() < v2.toInt();
}
};

Set operations not working for Custom Object

So I have a custom object called WaterJug and I'm getting errors when calling functions on a global set<WaterJug>:
This is defined before the class definition:
set<WaterJug> memo;
And in one of the methods of my class:
for(std::vector<WaterJug>::iterator newJug = newJugs.begin(); newJug != newJugs.end(); ++newJug) {
const bool is_in = memo.find(newJug); //error here
if (is_in) {
}
}
Error is: No matching member function for call to 'find'
Do I have to implement any interfaces in my custom class for the set operations to work?
You have couple of errors.
Wrong argument being passed to memo.find.
memo.find(newJug) // tpe of newJug is an iterator, not an object.
It needs to be
memo.find(*newJug)
The return value of memo.find() is not a bool. It is an iterator. Instead of
const bool is_in = memo.find(newJug); //error here
if (is_in) {
use:
if ( memo.find(*newJug) != memo.end() )
std::set::find takes an object, newJug is an an iterator, not an object, to access the object "pointed" by the iterator, you need to dereference newJug: *newJug:
Moreover, find returns an iterator, not a bool, it returns end() iterator of object os not found.
This version of the code will work:
for(std::vector<WaterJug>::iterator newJug = newJugs.begin(); newJug != newJugs.end(); ++newJug) {
const bool is_in = ( memo.find(*newJug) != memo.end() );
if ( is_in ) {
}
}
std::set::find only takes parameter of the key type of the set. When you use memo.find(newJug) you are calling find with an iterator to a key and not a key. You can dereference the iterator to get a key value memo.find(*newJug);.
You also have an issue with the fact that you are trying to store the return from find as a bool when find returns an iterator. If you want to know if find found something then you can rewrite
const bool is_in = memo.find(newJug);
To
const bool is_in = (memo.find(newJug) != memo.end());