C++ overriding default less than argument for std::set for a particular data type

Viewed 85

I have lots of code which creates a std::set<Port*>. Now by default this uses less<Port*> which sorts the set on pointer values. This creates problem in our tool. I want to provide a default comparator my_less for all set<Port*> without having to go and change all the code. So I tried template specialization :

namespace std {
    template<> class set<Port*> : public set<Port*, my_less> {};
};

I put this code in a header file already included by all the code files. This works the way I expected that all set<Port*> are now using my_less. The problem is that such specialization is not inheriting constructors. So i get an error if I want to do this :

std::set<Port*> myset_new(myset1.begin(), myset1.end());

set.cpp:53:63: error: no matching function for call to
‘std::set<Port*>::set(std::set<Port*, znl_id_less>::iterator, std::set<Port*, my_less>::iterator)’
    set_include.h:27:70: note: candidates are: std::set<Port*>::set()
    set_include.h:27:70: note:                 std::set<Port*>::set(const std::set<Port*>&)

What is a good way to solve this (I can copy the code for constructors but that doesn't seem clean).

Is there any other way to achieve what I am trying to do apart from:

template<> class set<Port*> : public set<Port*, my_less> {};
2 Answers

Since C++11, you can inherit constructors easily with the using-declaration, like this:

namespace std {
    template<> class set<Port*> : public set<Port*, my_less> {
        public: using set<Port*, my_less>::set;
    };
};

Here is my generic solutions to this problem

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

// structs
struct my_struct
{
    int a;
    int b;
};

struct my_second_struct
{
    int a;
    int b;
};

bool operator<(const my_second_struct& m1, const my_second_struct& m2)
{
    return m1.b < m2.b;
}

// alias helper
template <typename T>
using custom_comparator_t = std::function<bool(const T, const T)>;

// function to proxy already defined operator to pointers
template<typename T>
auto get_pointer_less()
{
    return [](const T* v1, const T* v2) -> bool { return (*v1) < (*v2); };
}

// this is generic solution
template<typename T>
using pointer_set = std::set<T*, custom_comparator_t<T*>>;

// and final config
template<typename T>
auto get_pointer_set()
{
    return pointer_set<T>( get_pointer_less<T>() );
}

template<typename T, typename _T>
void fill_and_display(_T& s)
{
    s.insert(new T{10, 20});
    s.insert(new T{20, 10});
    s.insert(new T{0, 100});
    s.insert(new T{100, 0});

    std::cout << "set: ";
    for (const T *ms : s)
        std::cout << "( " << ms->a << " ; " << ms->b << " )";
    std::cout << std::endl;
}

int main()
{
    // First option, create custom less operator inline
    std::set<my_struct *, custom_comparator_t<my_struct *>> myset(
        [](const my_struct *ms1, const my_struct *ms2) { return ms1->a < ms2->a; }
    );

    fill_and_display<my_struct>(myset);

    // Second option, reuse, already defined less operator
    std::set<my_second_struct *, custom_comparator_t<my_second_struct*>> my_second_set(
        get_pointer_less<my_second_struct>()
    );

    fill_and_display<my_second_struct>(my_second_set);

    // generic colution for second set
    auto my_third_set = get_pointer_set<my_second_struct>();

    fill_and_display<my_second_struct>(my_third_set);

    return 0;
}
Related