Search code examples
c++stdset

std::set Comparator that refer to external value


I have something like

class ClassB {
    // ....
private:
    static std::unordered_map<ClassA,double> activity;
    struct compare_values {
        bool operator()(const ClassA& l, const ClassA& r) const {
            return activity[l] < activity[r];
        }
    };
    std::set<ClassA,compare_values> ordered;
    // ...
}

Because I need ClassB to return the ClassA object with the maximum activity (only from the object in ordered), remove some ClassA object from the ordered set and insert new one inside.

How can I remove the static requirement for the unoredered_map? I want that every ClassB object have a compare_values that reference to the specific activity hash map, but I have no idea how to pass the map to them.


Solution

  • Here's one way. Note that I have made the comparator copy/moveable with the use of an observer pointer rather than a reference:

    #include <unordered_map>
    #include <set>
    #include <memory>
    
    struct ClassA {};
    std::size_t hash_value(ClassA const&);
    bool operator==(ClassA const&, ClassA const&);
    
    namespace std {
        template<>
        struct hash<::ClassA> {
            template<class Arg>
            auto operator()(Arg&& arg) const {
                return hash_value(arg);
            }
        };
    }
    
    class ClassB {
    
    private:
        using activity_map = std::unordered_map<ClassA,double>;
    
        struct compare_values 
        {
            compare_values(activity_map const& activity)
            : activity_observer_(std::addressof(activity))
            {}
    
            bool operator()(const ClassA& l, const ClassA& r) const {
                return activity().at(l) < activity().at(r);
            }
    
        private:
    
            auto activity() const -> activity_map const&
            {
                return *activity_observer_;
            }
    
            activity_map const* activity_observer_;
        };
        activity_map activity {};
        std::set<ClassA,compare_values> ordered { compare_values { activity } };
        // ...
    };
    
    int main()
    {
        ClassB b;
    }
    

    by request, refactored:

    #include <unordered_map>
    #include <set>
    #include <memory>
    
    struct ClassA {};
    std::size_t hash_value(ClassA const&);
    bool operator==(ClassA const&, ClassA const&);
    
    namespace std {
        template<>
        struct hash<::ClassA> {
            template<class Arg>
            auto operator()(Arg&& arg) const {
                return hash_value(arg);
            }
        };
    }
    
    using ClassA_activity_map = std::unordered_map<ClassA, double>;
    
    struct by_ascending_classA_activity 
    {
        by_ascending_classA_activity(ClassA_activity_map const& activity)
        : activity_observer_(std::addressof(activity))
        {}
    
        bool operator()(const ClassA& l, const ClassA& r) const {
            return activity().at(l) < activity().at(r);
        }
    
    private:
    
        auto activity() const -> ClassA_activity_map const&
        {
            return *activity_observer_;
        }
    
        ClassA_activity_map const* activity_observer_;
    };
    
    
    class ClassB {
    
    private:
    
        ClassA_activity_map activity {};
        std::set<ClassA, by_ascending_classA_activity> ordered { { activity } };
        // ...
    };
    
    int main()
    {
        ClassB b;
    }