Search code examples
c++c++11setcustom-compare

std::set of MyElement with MyElement::SomeMethod as custom comparator


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

Solution

  • 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; }
    };
    
    // Define make function to avoid having to write out template types.
    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;
    }