Home > Back-end >  std::set of MyElement with MyElement::SomeMethod as custom comparator
std::set of MyElement with MyElement::SomeMethod as custom comparator

Time:12-30

I have a simple MyElement class, and I would like to use a bool MyElement::SomeMethod(...) {...} as the custom comparator for a std::set of MyElement items.

I have made my research, and I am already aware of some alternative solutions, which I list below. I also know how to change, for example, the comparator with std::greater instead of the default std::less, with code like this:

std::set<MyElement, std::greater<MyElement> > s;

My exact problem is that I want to use bool MyElement::SomeMethod(...) {...} as custom comparator. The only solution I come up with is analogous to the last one in the list below, namely the solution for boolean function:

using Cmp = std::integral_constant<decltype(&MyElement::SomeMethod), 
                                   &MyElement::SomeMethod>;
std::set<MyElement, Cmp> my_EventInSet_set;

This solution only works for a static MyElement::SomeMethod, though.

I am wondering if there is an analgous, or more concise, way for a non static method.

List of alternative solutions:

method for C 20

auto cmp = [](const MyElement& lhs, const MyElement& rhs) { return ... };
std::set<MyElement, decltype(cmp)> s;

method for C 11

auto cmp = [](const MyElement& lhs, const MyElement& rhs) { return ... };
std::set<MyElement, decltype(cmp)> s(cmp);

function instead of a lambda

 bool cmp(const MyElement& lhs, const MyElement& rhs) { return ...; }

and then

std::set<MyElement, decltype(cmp)*> s(cmp);

or

std::set<int, decltype(&cmp)> s(&cmp);

struct and operator()

struct cmp {
    bool operator() (const MyElement& lhs, const MyElement& rhs) const {
        return ...
    }
};

and then

std::set<MyElement, cmp> s;

boolean function

bool cmp(const MyElement& lhs, const MyElement& rhs) {
    return ...;
}

and then

#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;
std::set<MyElement, Cmp> s;

CodePudding user response:

This is a bit subjective, but to me the cleanest option is struct operator() to match the definition of std::less, the default comparator for std::set. There's nothing wrong with the other options but a comparison functor is a common pattern and easy to recognize.

You could also define MyElement::operator<, and then you wouldn't need to pass in a comparator separately.

CodePudding user response:

You can use std::mem_fn to bind a member function.

#include <functional>
#include <iostream>
#include <set>
#include <utility>

struct S {
  int i;

  bool cmp(const S& other) const { return i < other.i; }
};

template <typename T, typename Cmp>
std::set<T, Cmp> make_set(Cmp&& cmp) {
  return std::set<T, Cmp>{std::forward<Cmp>(cmp)};
}

int main(int argc, char* argv[]) {
  auto s = make_set<S>(std::mem_fn(&S::cmp));
  s.emplace(S{0});

  std::cout << s.begin()->i << std::endl;
  return 0;
}
  • Related