Home > OS >  Passing external data to std::set Compare functor
Passing external data to std::set Compare functor

Time:11-09

This is a contrived example, I know it doesn't make sense in this context. In my real case, v contains a larger class, and I can't refactor v due to other dependencies in the code base. Also, in the real case I'm not using std::set but it simplifies the example. Here are two example classes A and B that I know don't work, and I do understand why, but hopefully will illustrate what I'm trying to do:

#include <set>
#include <vector>

using namespace std;

struct A
{
    A(initializer_list<float> init) : v(init)
    {
        for(size_t i = 0; i < init.size();   i)
        {
            s.insert(i);
        }
    }

    struct Less
    {
        Less(const vector<float>& v) : v(v) {};

        bool operator()(size_t i, size_t j)
        {
            return v[i] < v[j];
        }

        const vector<float>& v;
    };

    vector<float> v;
    set<size_t, Less> s;
};

struct B
{
    B(initializer_list<float> init) : v(init)
    {
        for(size_t i = 0; i < init.size();   i)
        {
            s.insert(i);
        }
    }

    template<const vector<float>& v>
    struct Less
    {
        bool operator()(size_t i, size_t j)
        {
            return v[i] < v[j];
        }
    };

    vector<float> v;
    set<size_t, Less<v>> s;
};

int main()
{
    A a = {1., 2., 3.};
    B b = {1., 2., 3.};
}

In both cases s contains indicies that refer to elements in v, and Less is trying to compare the values in v using those indices. Is there a way to do this that I'm not seeing?

CodePudding user response:

Your A approach is almost correct. It just needs 2 fixes:

  • bool operator()(size_t i, size_t j) should be const.
  • s must have a configured functor passed to it during construction.
struct A
{
    A(initializer_list<float> init) : v(init), s(v)
    {
        for(size_t i = 0; i < init.size();   i)
        {
            s.insert(i);
        }
    }

    // These must be manually defined if you need them, the default
    // ones won't be correct.
    A(const A&) = delete; 
    A& operator=(const A&) = delete;

    struct MyLess
    {
        MyLess(const vector<float>& v) : v(v) {};

        bool operator()(size_t i, size_t j) const
        {
            return v[i] < v[j];
        }

        const vector<float>& v;
    };

    vector<float> v;
    set<size_t, MyLess> s;
};

I'm using unordered_map so maybe your idea will still work?

Yes, the same principle applies to std::unordered_set. You just have to pass in both hash and key_equal during construction. Check out the documentation to identify the correct constructor to invoke.

  •  Tags:  
  • c
  • Related