User-defined class in std::map: Invalid operands to binary expression - c++

I want to use a class as the key in a std::map.
std::map<Type, value> collection;
Although I defined operator<, the key isn't accepted: Invalid operands to binary expression ('const Type' and 'const Type').
class Type {
public:
inline bool operator< (const Type& rhs) { /* ... */ }
Why is this happening?

You must define your operator< as
inline bool operator< (const Type& rhs) const { /* ... */ }
because map stores const key internally.
To expand a bit I'd also suggest (like dyp already did in the comments) That you use non-member operator overloads when possible. There are various advantages to them. I won't list them here since there already is good information on the advantages/differences, just let me link you to Operator overloading : member function vs. non-member function?.

Related

`std::remove` in template function causing problems with `vector.begin()` being `const`

The error message:
"binary '==': no operator found which takes a left-hand operand of type 'const _Ty' (or there is no acceptable conversion)"
The error seems to be because I'm giving std::remove a const value for the first argument, but I don't see exactly how v.begin() being unmodifiable would cause problems, nor why this error only occurs if the type of the vector is templated, as opposed to a known type vector.
The template function:
// Erases a every element of a certain value from a vector
template <typename T>
std::vector<T> eraseElement(std::vector<T> v, T elem)
{
typename std::vector<T>::iterator newEnd = std::remove(v.begin(), v.end(), elem);
v.erase(newEnd, v.end());
return v;
}
An example of the way I'm calling the template function:
struct exampleStruct
{
int x;
}
std::vector<exampleStruct> v;
exampleStruct elem;
elem.x = 2451;
v.push_back(elem);
v = eraseElement(v, elem);
nor why this error only occurs if the type of the vector is templated, as opposed to a known type vector.
It also occurs if you substitute exampleStruct manually.
std::vector<exampleStruct> eraseElement(std::vector<exampleStruct> v, exampleStruct elem)
{
auto newEnd = std::remove(v.begin(), v.end(), elem);
v.erase(newEnd, v.end());
return v;
}
That's because there isn't an operator== for exampleStruct. You probably want something like
bool operator==(const exampleStruct & lhs, const exampleStruct & rhs)
{
return lhs.x == rhs.x;
}
or with a C++20 implementation
struct exampleStruct
{
int x;
friend bool operator==(const exampleStruct &, const exampleStruct &) = default;
};
The error message: "binary '==': no operator found which takes a left-hand operand of type 'const _Ty' (or there is no acceptable conversion)"
The error seems to be because I'm giving std::remove a const value for the first argument
Probably not. The error is because you're probably using T that is not equality comparable (in other words, there is no equality operator - which is the binary operator== - that would accept the operands). Constness should not be a problem because an equality comparable type should accept const arguments for comparison.
exampleStruct in particular is not equality comparable. One way to solve your problem is to make it equality comparable. You can do that by defining the binary operator== for the type. I recommend following approach:
struct exampleStruct
{
int x;
friend auto operator<=>(const exampleStruct&, const exampleStruct&) = default;
};
Another way to solve the problem is to not use std::remove, but to use std::remove_if instead. That way you can provide the condition to remove the element which doesn't have to compare the elements for their equality.
nor why this error only occurs if the type of the vector is templated, as opposed to a known type vector.
I highly doubt this. exampleStruct remains non-comparable whether eraseElement is a template or not.

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.

How to define boost::any operator ==

I want to define operator == for boost::any in my project. Since the arguments belong to the boost namespace, this is where argument-dependent lookup will search for it. So, the signature is:
namespace boost
{
bool operator == (const boost::any &, const boost::any &);
}
However, this generates ambiguous overload errors whenever I include a boost library that compares enums for equality, such as thread/locks.hpp — the compiler sees no reason to prefer converting the enums to int and using the built-in comparison instead of converting them to boost::any and using mine.
I can hack around this by also including any such libraries in the same file as my comparator and defining custom operators for comparing boost's enums. But there got to be a better way, right?
namespace boost {
template<class T,
typename std::enable_if<std::is_same<T, any>{}, bool>::type =true
>
bool operator == (const T& lhs, const T& rhs){
return any_equal(lhs, rhs);
}
}

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++: Overloading the [ ] operator for read and write access

In general, how do you declare the index [ ] operator of a class for both read and write accesss?
I tried something like
/**
* Read index operator.
*/
T& operator[](T u);
/**
* Write index operator
*/
const T& operator[](T u);
which gives me the error
../src/Class.h:44:14: error: 'const T& Class::operator[](T)' cannot be overloaded
../src/Class.h:39:8: error: with 'T& Class::operator[](T)'
Your mutable version is fine:
T& operator[](T u);
but the const version should be a const member function as well as returning a const reference:
const T& operator[](T u) const;
^^^^^
This not only distinguishes it from the other overload, but also allows (read-only) access to const instances of your class. In general, overloaded member functions can be distinguished by their parameter types and const/volatile qualifications, but not by their return types.
You simply have one overload that'll be used for both reads and writes:
T& operator[](int);
Having said that, you might also want to have a const overload:
const T& operator[](int) const;
This will provide read-only indexing into const instances of your class.
You get the error, because overloaded functions cannot differ only by return type. But they can differ by const-ness.
/**
* Write index operator.
*/
T& operator[](T u);
/**
* Read index operator
*/
const T& operator[](T u) const;
Note "write" and "read" swapped places.
Also, don't you actually mean the argument of the operator to be some integral type ?