Search code examples
c++templatestemplate-templates

Throw multiple-template class in a template template parameter - template binding?


Given the following class:

template <class T, template <typename> class B>
class A { B<T> b; };

I can now write code like such:

A<float, MyVector> a1;
A<int, MySet> a2;

What is the most elegant way to put multi-parameter classes of which all parameters are specified except one, in B? Like a map with int-keys? The only thing I can come up with is this:

template <class U> using C = MyMap<int, U>;
A<float, C<int>> a3;

Is there such a template equivalent to std::bind, where we can provide only a part of the parameters and leave one of them open? I'm quite sure the language doesn't provide for this, but people must've solved this before.

A<float, MyMap<int, _>> a3;

Solution

  • There isn't a built-in template equivalent to std::bind, but you can write one yourself. Here's a simple version which binds the first template argument which you could extend to suit your needs:

    template <typename T, template <typename...> class B>
    struct bind_t1 {
        template <typename... Ts>
        using type = B<T,Ts...>;   
    };
    

    Then you just use bind_t1 like so:

    A<float, bind_t1<int, std::map>::type> a3;
    

    Note that for your example, you'll need to modify your template parameters to take a variadic template template:

    template <class T, template <typename...> class B>
    class A { B<T> b; };
    

    Here's a slightly extended version which can bind a number of contiguous elements at the start of the parameter list:

    template <template <typename...> class B, typename... Ts>
    struct bind_nt1 {
        template <typename... Us>
        using type = B<Ts...,Us...>;   
    };
    
    //Usage
    A<std::less<int>, bind_nt1<std::map, int, float>::type> a3;
    

    Here's a generic version based on the way std::bind does things. It doesn't do any validation and probably has some edge cases, but it's a good starting point. Thanks to Piotr Skotnicki for improvements.

    template <std::size_t N> 
    struct placeholder{};
    
    template <template <typename...> class B, typename... Ts>
    struct bind_t {
    private:
        template <typename T, typename UTuple>
        struct resolve_placeholder {
            using type = T;
        };
    
        template <std::size_t N, typename UTuple>
        struct resolve_placeholder<placeholder<N>, UTuple> {
            using type = typename std::tuple_element<N-1, UTuple>::type;
        };
    
    public:
        template <typename... Us>
        using type = B<typename resolve_placeholder<Ts, std::tuple<Us...>>::type...>;
    };
    
    
    //Usage 
    A<int, bind_t<std::map, float, placeholder<1>, std::less<float>>::type> a3;
    

    Using this, you can even change the order of template parameters:

    //std::map<int,float>
    bind_t<std::map, placeholder<2>, placeholder<1>>::type<float, int> b;