I struggle with a std::set that should support custom compare operations. My goal would be to be able to call find with just the key argument, without first creating an object of the set type. Something like this:
#include <set>
#include <cstdint>
#include <vector>
class TestItem {
public:
std::uint64_t id; // key
std::vector<double> areas; // some other data
};
struct TestItemCompare {
bool operator()(const std::uint64_t& lhs, const std::uint64_t& rhs) {
return lhs < rhs;
}
bool operator()(const std::uint64_t& lhs, const TestItem& rhs) {
return lhs < rhs.id;
}
bool operator()(const TestItem& lhs, const std::uint64_t& rhs) {
return lhs.id < rhs;
}
bool operator()(const TestItem& lhs, const TestItem& rhs) {
return lhs.id < rhs.id;
}
};
int main() {
std::set<TestItem, TestItemCompare> store;
std::uint64_t id = 0;
TestItem t;
t.id = 0;
auto it1 = store.find(t); // compiles
auto it2 = store.find(id); // fails to compile
return 0;
}
I thought this was possible...
It is possible. But your comparator must be a transparent one. And the member functions in functor need to be const
struct TestItemCompare {
using is_transparent = int; // actually any alias will do, void, custom type
bool operator()(const std::uint64_t& lhs, const std::uint64_t& rhs) const {
return lhs < rhs;
}
bool operator()(const std::uint64_t& lhs, const TestItem& rhs) const {
return lhs < rhs.id;
}
bool operator()(const TestItem& lhs, const std::uint64_t& rhs) const {
return lhs.id < rhs;
}
bool operator()(const TestItem& lhs, const TestItem& rhs) const {
return lhs.id < rhs.id;
}
};
i have a custom struct i am mapping to some data using a boost::bimap.
Unfortunately, the bimap find() does not work as expected.
Below is a minimal example to demonstrate:
#include <utility>
#include <boost/bimap.hpp>
struct S{
int a;
int b;
};
bool operator<(const S& lhs, const S& rhs) {
return std::tie(lhs.a, lhs.b) <
std::tie(lhs.a, lhs.b);
}
bool operator==(const S& lhs, const S& rhs) {
return
lhs.a == rhs.a &&
lhs.b == rhs.b;
}
int main() {
boost::bimap<S, int> bmap;
S s0{0, 0};
S s1{0, 1};
bmap.left.insert(std::make_pair(s0, 0));
bmap.left.insert(std::make_pair(s1, 1));
auto it0 = bmap.left.find(s0);
assert(it0 != bmap.left.end());
auto res0_s = it0->first;
auto res0 = it0->second;
assert(res0_s == s0);
assert(res0 == 0);
auto it1 = bmap.left.find(s1);
assert(it1 != bmap.left.end());
auto res1_s = it1->first;
auto res1 = it1->second;
assert(res1_s == s1);
assert(res1 == 1);
}
The last two asserts are failing (gdb shows that res1_s == s0). I suspect the implementation of operator< is somehow not working as intended. As i understand std::tie, it should just lexicographically compare the two operands and that should suffice to use arbitrary structs as a key for maps.
Thanks for your help.
bool operator<(const S& lhs, const S& rhs) {
return std::tie(lhs.a, lhs.b) <
std::tie(lhs.a, lhs.b);
}
You've given the same arguments to both calls of std::tie functions, so your operator< always returns false. Change it to following.
bool operator<(const S& lhs, const S& rhs) {
return std::tie(lhs.a, lhs.b) <
std::tie(rhs.a, rhs.b);
}
In C++, for the operator greater than or equal to (">="), is it enough to have the operators equal ("=") and greater (">") overloaded to have functionality for the greater than or equal to (">=")? Or do I need to overload the operator (">=") to have functionality for it?
operator >= is not a combination of operator > and operator =. operator >= is its own operator but you can implement it in terms of operator < Typically you would have something like
inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}
From sbi's answer on What are the basic rules and idioms for operator overloading?
is it enough to have the operators equal ("=")
Equal operator in c++ is ==
OR do I need to overload the operator (">=") to have functionality for it?
It depends what you mean by functionality. If you mean that if you define operator== and operator> will compiler generate operator>= automagically for you? No, it would not, you have to implement it using existing operators or not.
No, C++ does not write those operators for you.
If you think that sucks, you are right. A bunch of ways to make this suck less have been done. I'll talk about 4 of them.
Wait for c++20
In c++20, if you write operator<=> (the 3-way "spaceship" operator) properly, or =default it, then all of <, <=, >=, >, != and == will be written for you.
struct bob {
int x,y;
auto operator<=>( bob const& )const = default;
};
The above bob has every < == etc operator written for it by C++ now.
Just write them
Prior to c++20 you have to write all of them if you want all of them. This is tedious and error-prone.
Using std::tie and invoking < and the like on them is slightly less error-prone:
struct bob {
int x, y;
friend bool operator<( bob const& lhs, bob const& rhs ) {
return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
}
};
or even
struct bob {
int x, y;
friend auto as_tie( bob const& b ) { // C++14
return std::tie(b.x, b.y);
}
friend bool operator<( bob const& lhs, bob const& rhs ) {
return as_tie(lhs) < as_tie(rhs);
}
};
because tuple does a proper lexographic comparison; writing lexographic comparions without bugs is annoying.
Metaprogram your way around it
When comparing strings you usually use strcmp. This returns a negative number if less, a positive number if greater, and 0 if equal. This pattern can be more efficient than doing < or == repeatedly.
Making a single strcmp like function produce < == and the other comparison operations can be done:
namespace utils {
template<class D>
struct use_cmp {
friend bool operator<( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) < 0;
}
friend bool operator>( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) > 0;
}
friend bool operator<=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) <= 0;
}
friend bool operator>=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) >= 0;
}
friend bool operator==( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) == 0;
}
friend bool operator!=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
return cmp( lhs.self(), rhs.self() ) != 0;
}
private:
D const& self() const { return *static_cast<D const*>(this); }
};
}
Now supose we have a type:
struct bob {
int x, y;
};
and we want to be able to use comparison operators on it:
struct bob : utils::use_cmp<bob>
{
int x, y;
bob( int x_, int y_ ):x(x_), y(y_) {} // constructor
friend int cmp( bob const& lhs, bob const& rhs ) {
if (lhs.x < rhs.x) return -1;
if (lhs.x > rhs.x) return 1;
if (lhs.y < rhs.y) return -1;
if (lhs.y > rhs.y) return 1;
return 0;
}
};
and using the magic of CRTP bob now has every comparison operator written for it.
Live example.
That annoying friend int cmp (which gets more annoying the more members you have in it) can be handled by yet more boilerplate helper code:
namespace utils {
template<class...Ts>
int cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs );
template<class T, class...LowPriority>
int cmp( T const& lhs, T const& rhs, LowPriority&&... );
template<class...Ts, std::size_t...Is>
int tuple_cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs, std::index_sequence<Is...> ) {
int result = 0;
( (result = cmp( std::get<Is>(lhs), std::get<Is>(rhs) )) && ... );
return result;
}
template<class...Ts>
int cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs ) {
return tuple_cmp( lhs, rhs, std::make_index_sequence<sizeof...(Ts)>{} );
}
template<class T, class...LowPriority>
int cmp( T const& lhs, T const& rhs, LowPriority&&... ) {
if (lhs < rhs) return -1;
if (rhs < lhs) return 1;
return 0;
}
}
which is yet more arcane code, but you get a simpler bob:
struct bob : utils::use_cmp<bob>
{
int x, y;
bob( int x_, int y_ ):x(x_), y(y_) {}
friend auto as_tie(bob const& b) {
return std::tie(b.x,b.y);
}
friend int cmp( bob const& lhs, bob const& rhs ) {
return utils::cmp( as_tie(lhs), as_tie(rhs) );
}
};
Note, however, that all of this is done and better by operator<=> in c++20.
Live example.
Use someone else's solution
This is similar to the approach boost::operators uses to write these operators for you.
Using an obvious notation, "> || ==" is actually an over-requirement for >=.
Although note that for all the relational operators, you only actually need <, since equivalence is established if a < b and b < a are both false. In fact this is one of the concepts used in ordered C++ standard library containers.
I've never implemented STL-like iterators and I try to understand how to implement a very basic thing based on pointers. Once I will have this class I will be able to modify it to do more complicated things. Consequently, this is a first step, and I need it to be rock solid to understand how to write my own iterators (without boost).
I have written the following code and I know that there are errors in it. Can you help me to design correctly a Random Access Iterator class inspired from that :
template<Type> class Container<Type>::Iterator : public std::iterator<random_access_iterator_tag, Type>
{
// Lifecycle:
public:
Iterator() : _ptr(nullptr) {;}
Iterator(Type* rhs) : _ptr(rhs) {;}
Iterator(const Iterator &rhs) : _ptr(rhs._ptr) {;}
// Operators : misc
public:
inline Iterator& operator=(Type* rhs) {_ptr = rhs; return *this;}
inline Iterator& operator=(const Iterator &rhs) {_ptr = rhs._ptr; return *this;}
inline Iterator& operator+=(const int& rhs) {_ptr += rhs; return *this;}
inline Iterator& operator-=(const int& rhs) {_ptr -= rhs; return *this;}
inline Type& operator*() {return *_ptr;}
inline Type* operator->() {return _ptr;}
inline Type& operator[](const int& rhs) {return _ptr[rhs];}
// Operators : arithmetic
public:
inline Iterator& operator++() {++_ptr; return *this;}
inline Iterator& operator--() {--_ptr; return *this;}
inline Iterator& operator++(int) {Iterator tmp(*this); ++_ptr; return tmp;}
inline Iterator& operator--(int) {Iterator tmp(*this); --_ptr; return tmp;}
inline Iterator operator+(const Iterator& rhs) {return Iterator(_ptr+rhs.ptr);}
inline Iterator operator-(const Iterator& rhs) {return Iterator(_ptr-rhs.ptr);}
inline Iterator operator+(const int& rhs) {return Iterator(_ptr+rhs);}
inline Iterator operator-(const int& rhs) {return Iterator(_ptr-rhs);}
friend inline Iterator operator+(const int& lhs, const Iterator& rhs) {return Iterator(lhs+_ptr);}
friend inline Iterator operator-(const int& lhs, const Iterator& rhs) {return Iterator(lhs-_ptr);}
// Operators : comparison
public:
inline bool operator==(const Iterator& rhs) {return _ptr == rhs._ptr;}
inline bool operator!=(const Iterator& rhs) {return _ptr != rhs._ptr;}
inline bool operator>(const Iterator& rhs) {return _ptr > rhs._ptr;}
inline bool operator<(const Iterator& rhs) {return _ptr < rhs._ptr;}
inline bool operator>=(const Iterator& rhs) {return _ptr >= rhs._ptr;}
inline bool operator<=(const Iterator& rhs) {return _ptr <= rhs._ptr;}
// Data members
protected:
Type* _ptr;
};
Thank you very much.
Your code has the following issues:
You do not follow the Rule of Three/Five. The best option in your situation is not to declare any custom destructors, copy/move constructors or copy/move assignment operators. Let's follow so called Rule of Zero.
Iterator(Type* rhs) could be private and the Container could be marked as Iterator's friend, but that's not strictly necessary.
operator=(Type* rhs) is a bad idea. That's not what type safety is about.
Post-in(de)crementation should return Iterator, not Iterator &.
Adding two iterators has no meaning.
Subtracting two iterators should return a difference, not a new iterator.
You should use std::iterator<std::random_access_iterator_tag, Type>::difference_type instead of const int &.
If a method does not modify an object, it should be marked const.
Useful resource: RandomAccessIterator # cppreference.com
Here is a fixed version of your code:
template<typename Type>
class Container<Type>::Iterator : public std::iterator<std::random_access_iterator_tag, Type>
{
public:
using difference_type = typename std::iterator<std::random_access_iterator_tag, Type>::difference_type;
Iterator() : _ptr(nullptr) {}
Iterator(Type* rhs) : _ptr(rhs) {}
Iterator(const Iterator &rhs) : _ptr(rhs._ptr) {}
/* inline Iterator& operator=(Type* rhs) {_ptr = rhs; return *this;} */
/* inline Iterator& operator=(const Iterator &rhs) {_ptr = rhs._ptr; return *this;} */
inline Iterator& operator+=(difference_type rhs) {_ptr += rhs; return *this;}
inline Iterator& operator-=(difference_type rhs) {_ptr -= rhs; return *this;}
inline Type& operator*() const {return *_ptr;}
inline Type* operator->() const {return _ptr;}
inline Type& operator[](difference_type rhs) const {return _ptr[rhs];}
inline Iterator& operator++() {++_ptr; return *this;}
inline Iterator& operator--() {--_ptr; return *this;}
inline Iterator operator++(int) const {Iterator tmp(*this); ++_ptr; return tmp;}
inline Iterator operator--(int) const {Iterator tmp(*this); --_ptr; return tmp;}
/* inline Iterator operator+(const Iterator& rhs) {return Iterator(_ptr+rhs.ptr);} */
inline difference_type operator-(const Iterator& rhs) const {return _ptr-rhs.ptr;}
inline Iterator operator+(difference_type rhs) const {return Iterator(_ptr+rhs);}
inline Iterator operator-(difference_type rhs) const {return Iterator(_ptr-rhs);}
friend inline Iterator operator+(difference_type lhs, const Iterator& rhs) {return Iterator(lhs+rhs._ptr);}
friend inline Iterator operator-(difference_type lhs, const Iterator& rhs) {return Iterator(lhs-rhs._ptr);}
inline bool operator==(const Iterator& rhs) const {return _ptr == rhs._ptr;}
inline bool operator!=(const Iterator& rhs) const {return _ptr != rhs._ptr;}
inline bool operator>(const Iterator& rhs) const {return _ptr > rhs._ptr;}
inline bool operator<(const Iterator& rhs) const {return _ptr < rhs._ptr;}
inline bool operator>=(const Iterator& rhs) const {return _ptr >= rhs._ptr;}
inline bool operator<=(const Iterator& rhs) const {return _ptr <= rhs._ptr;}
private:
Type* _ptr;
};
In general your approach is right. The postfix increment/decrement operator should return by value, not by reference. I also have doubts about:
Iterator(Type* rhs) : _ptr(rhs) {;}
This tells everyone that this iterator class is implemented around pointers. I would try making this method only callable by the container. Same for assignment to a pointer.
Adding two iterators makes no sense to me (I would leave "iterator+int"). Substracting two iterators pointing to the same container might make some sense.
Have a look at how Boost do it, the iterators in boost/container/vector.hpp - vector_const_iterator and vector_iterator are reasonably easy to understand pointer based iterators.
I've implemented the functionality of std::rel_ops namespace as a template base class (it defines all comparison operators using only operators < and ==). For me it's a bit weird that it works (so far) properly, also I'm concerned about the 'hacks' used. Can anyone assess the following code and say if I'm just lucky it to work or it's standard practice to do things like that.
template <typename T>
class RelationalOps {
public:
inline bool operator!=(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return !(lhs == rhs);
}
inline bool operator<=(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return ((lhs < rhs) || (lhs == rhs));
}
inline bool operator>(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return !((lhs < rhs) || (lhs == rhs));
}
inline bool operator>=(const T &rhs) const
{
const T& lhs = static_cast<const T&>(*this);
return !(lhs < rhs);
}
};
Well, why not use Boost.Operators?