Using custom std::set comparator

后端 未结 5 1493
耶瑟儿~
耶瑟儿~ 2020-11-22 16:03

I am trying to change the default order of the items in a set of integers to be lexicographic instead of numeric, and I can\'t get the following to compile with g++:

相关标签:
5条回答
  • 2020-11-22 16:37

    1. Modern C++20 solution

    auto cmp = [](int a, int b) { return ... };
    std::set<int, decltype(cmp)> s;
    

    We use lambda function as comparator. As usual, comparator should return boolean value, indicating whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.

    Online demo

    2. Modern C++11 solution

    auto cmp = [](int a, int b) { return ... };
    std::set<int, decltype(cmp)> s(cmp);
    

    Before C++20 we need to pass lambda as argument to set constructor

    Online demo

    3. Similar to first solution, but with function instead of lambda

    Make comparator as usual boolean function

    bool cmp(int a, int b) {
        return ...;
    }
    

    Then use it, either this way:

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

    Online demo

    or this way:

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

    Online demo

    4. Old solution using struct with () operator

    struct cmp {
        bool operator() (int a, int b) const {
            return ...
        }
    };
    
    // ...
    // later
    std::set<int, cmp> s;
    

    Online demo

    5. Alternative solution: create struct from boolean function

    Take boolean function

    bool cmp(int a, int b) {
        return ...;
    }
    

    And make struct from it using std::integral_constant

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

    Finally, use the struct as comparator

    std::set<X, Cmp> set;
    

    Online demo

    0 讨论(0)
  • 2020-11-22 16:45

    Yacoby's answer inspires me to write an adaptor for encapsulating the functor boilerplate.

    template< class T, bool (*comp)( T const &, T const & ) >
    class set_funcomp {
        struct ftor {
            bool operator()( T const &l, T const &r )
                { return comp( l, r ); }
        };
    public:
        typedef std::set< T, ftor > t;
    };
    
    // usage
    
    bool my_comparison( foo const &l, foo const &r );
    set_funcomp< foo, my_comparison >::t boo; // just the way you want it!
    

    Wow, I think that was worth the trouble!

    0 讨论(0)
  • 2020-11-22 16:59

    std::less<> when using custom classes with operator<

    If you are dealing with a set of your custom class that has operator< defined, then you can just use std::less<>.

    As mentioned at http://en.cppreference.com/w/cpp/container/set/find C++14 has added two new find APIs:

    template< class K > iterator find( const K& x );
    template< class K > const_iterator find( const K& x ) const;
    

    which allow you to do:

    main.cpp

    #include <cassert>
    #include <set>
    
    class Point {
        public:
            // Note that there is _no_ conversion constructor,
            // everything is done at the template level without
            // intermediate object creation.
            //Point(int x) : x(x) {}
            Point(int x, int y) : x(x), y(y) {}
            int x;
            int y;
    };
    bool operator<(const Point& c, int x) { return c.x < x; }
    bool operator<(int x, const Point& c) { return x < c.x; }
    bool operator<(const Point& c, const Point& d) {
        return c.x < d;
    }
    
    int main() {
        std::set<Point, std::less<>> s;
        s.insert(Point(1, -1));
        s.insert(Point(2, -2));
        s.insert(Point(0,  0));
        s.insert(Point(3, -3));
        assert(s.find(0)->y ==  0);
        assert(s.find(1)->y == -1);
        assert(s.find(2)->y == -2);
        assert(s.find(3)->y == -3);
        // Ignore 1234, find 1.
        assert(s.find(Point(1, 1234))->y == -1);
    }
    

    Compile and run:

    g++ -std=c++14 -Wall -Wextra -pedantic -o main.out main.cpp
    ./main.out
    

    More info about std::less<> can be found at: What are transparent comparators?

    Tested on Ubuntu 16.10, g++ 6.2.0.

    0 讨论(0)
  • 2020-11-22 17:01

    You are using a function where as you should use a functor (a class that overloads the () operator so it can be called like a function).

    struct lex_compare {
        bool operator() (const int64_t& lhs, const int64_t& rhs) const {
            stringstream s1, s2;
            s1 << lhs;
            s2 << rhs;
            return s1.str() < s2.str();
        }
    };
    

    You then use the class name as the type parameter

    set<int64_t, lex_compare> s;
    

    If you want to avoid the functor boilerplate code you can also use a function pointer (assuming lex_compare is a function).

    set<int64_t, bool(*)(const int64_t& lhs, const int64_t& rhs)> s(&lex_compare);
    
    0 讨论(0)
  • 2020-11-22 17:01

    You can use a function comparator without wrapping it like so:

    bool comparator(const MyType &lhs, const MyType &rhs)
    {
        return [...];
    }
    
    std::set<MyType, bool(*)(const MyType&, const MyType&)> mySet(&comparator);
    

    which is irritating to type out every time you need a set of that type, and can cause issues if you don't create all sets with the same comparator.

    0 讨论(0)
提交回复
热议问题