c++templatesstlmultiset

Different amount of template variables


I have to implement a class depending on std::multiset. The idea is that when two multisets get into the same "view", my class needs to sort them, make operators and iterators, etc., but I'm stuck on basically the first step. The problem is that I'd need to create the same class, just with different amount of template variables. The main program calls my class like this, for example:

multisets_merge_view<int> mvi(a, b); //a and b are std::multiset<int>
multisets_merge_view<int, std::greater<int>> mvi(ga, gb); //ga and gb are std::multiset<int, std::greater<int>>

I need to use the g++ compiler with -fsanitize=address,leak,undefined -O3 -Wall -Wextra -Werror


Solution

  • A pretty simple way to solve the issue would be providing a default argument for the comparator:

    template <typename T, typename C = std::less<T>>
    class multisets_merge_view { /* ... */ };
    

    Since C++17 you can rely on class template argument deduction, which can simplify usage of your template class pretty much – you don't even have to provide a deduction guide for:

    template <typename T, typename C>
    class multisets_merge_view 
    {
    public:
        multisets_merge_view (std::multiset<T, C>& x, std::multiset<T, C>& y);
    };
    
    // the deduction guide (but not necessary)
    template <typename T, typename C>
    multisets_merge_view(std::multiset<T, C>& x, std::multiset<T, C>& y)
        -> multisets_merge_view <T, C>;
    

    This allows using your class like:

    multisets_merge_view mvi(a, b);
    multisets_merge_view mvi_g(ga, gb);
    

    Note: There's no need to specify any template arguments at all any more...

    Yet another variant is having your view class totally generic:

    template <typename T>
    class generic_merge_view
    {
        using Compare = typename T::key_compare;
    public:
        generic_merge_view(T& x, T& y)
        {
            // just for demonstration
            Compare c;
            for(auto ix = x.begin(), iy = y.begin(); /*...*/; ++ix, ++iy)
            {
                if(c(*ix, *iy)) 
                {
                    // do something
                }
            } 
        }
    };
    

    You profit from duck-typing: As long as a type provides all the features the template requires – in this case the key_compare typedef and the ability to iterate – you can use it, this would comprise e.g. std::set, std::map and std::multimap as well.

    The template type then differs, though (it's the set or map itself, not just the set/map's template arguments), but with CTAD you don't need to care for...