Search code examples
c++templatesgeneric-programming

Template template collection accepting another template argument


I'm trying to wrap my head around how template templates work to implement a similar functionality as shown below.

Consider two classes (in Java). The first class is limiting the collection returned by pack method by specifying that only an object of the second template argument can be stored in it:

abstract class ContainerPacker<T, Container extends Collection<T>> {
    abstract Container pack(T t);
}

And implementation of this class for, let's say, Integer type could then look like this:

class IntegerContainerPacker extends ContainerPacker<Integer, List<Integer>> {
    @Override
    List<Integer> pack(Integer t) {
        List<Integer> list = new ArrayList<>(t);
        list.add(t);
        return list;
    }
}

Now I'd like to do something similar in C++ using template templates. Notice how the Container is a template itself:

template <typename T, template <typename U> class Container>
class ContainerPacker {
  public:
    virtual Container<T> pack(T) = 0;
};

I'm having trouble implementing it though. The code below does not compile:

class IntegerVectorPacker : public ContainerPacker<int, std::vector> {
  public:
    std::vector<int> pack(int t) {
        std::vector<int> v = std::vector<int>();
        v.push_back(t);
        return v;
    }
};

The errors are:

error: type/value mismatch at argument 2 in template parameter list for ‘template class Container> class ContainerPacker’ class IntegerVectorPacker : public ContainerPacker {

and

note: expected a template of type ‘template class Container’, got ‘template class std::vector’

I've been looking for an answer but it's even difficult to figure out what question to ask. Template templates are hard.


Solution

  • Try with

    template <typename T, template <typename...> class Container>
    

    The problem (a problem?) in your code is that std::vector is a template class that receive more than one templates argument (two: the second one has a default type); so doesn't match Container.

    Defining Container as receiving zero or more templates should permit the match with std::vector<int> (that really is std::vector<int, std::allocator<int>>) and other containers.

    -- EDIT --

    I see now that you have tagger this question C++ and not C++11 (or newer).

    The typename... suggestion is valid only starting from C++11 because variadic templates aren't available before.

    For C++98 you can write

    template <typename T, template <typename, typename> class Container>
    

    but this works only with two parameters containers.

    -- EDIT 2 --

    Off Topic suggestion.

    If you can use C++11 or newer (so variadic templates) switch the order of Container and T for ContainerPacker and transform T is a variadic pack; something like

    template <template <typename...> class Container, typename ... Ts>
    class ContainerPacker {
      public:
        virtual Container<Ts...> pack(Ts...) = 0;
    };
    

    It's much more flexible.