4

I'm trying to understand C++ template templates by implementing a generic container class. Here is the code:

    using namespace std;

    template <typename T, template <typename STORETYPE> class Container>
    class Store {
    public:
        ~Store() {};
        Store() {};
        void someFunc( const T & ) {};
        //...
    private:
        Container<T> storage;
    };




    int main(int argc, char *argv[])
    {
        Store<int,deque> myStore;   // error here, won't compile!
    }

The code above generates a compiler error message. The error message is:

"template template argument has different template parameters than its corresponding template template parameter Store aStack1;

I don't know why. What's wrong?

tzer
  • 280
  • 4
  • 11
  • 1
    If you pass deque only you can then use a deque trait to get its inner type (deque::value_type) instead of giving your container two template parameters, T and Container. – Robinson Jun 22 '15 at 10:47

3 Answers3

6

Your issue is that std::deque (and other standard containers) doesn't just take a single template argument. As well as the stored type, you can specify an allocator functor type to use.

If you don't care about these additional arguments, you can just take a variadic template template and be on your way:

template <typename T, template <typename...> class Container>
//                             variadic ^^^
class Store {

If you also want to support passing of optional arguments to your container type, you can forward them on like this:

template <template <typename ...> class Container, typename T, typename... ContainerArgs>
class Store {
    //...
    Container<T,ContainerArgs...> storage;
};

Then instantiate like so:

Store<deque,int> myStore;
Store<deque,int,MyIntAllocator> mySpecialStore;

However, you might just want to extract the template arguments using specialization:

template <typename Container>
class Store;

template <template <typename...> class ContainerType, typename T, typename... OtherArgs>
class Store<ContainerType<T,OtherArgs...>>
{
    //...
};

This will let client code instantiate like this:

Store<deque<int>> myStore;
Store<deque<int,MyIntAllocator>> mySpecialStore;
Store<T> myOtherStore; //where T is some specialized container type
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Note however that in the first case certain compilers (such as MSVC) will reject the instantiation of `Container` as they expect two template parameters for `std::deque`. – davidhigh Jun 22 '15 at 10:53
1

std::deque is defined as

template <class T, class Allocator = allocator<T> > class deque;

So you should change the definition of Store to match:

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

But actually you don't even need template template parameters for this. You can achieve even more generality just passing a container type as parameter, so for example Storage would support even std::map:

template <typename  Container>
class Store {
public:
    using value_type = typename Container::value_type;
    ~Store() {};
    Store() {};
    void someFunc( const value_type& ) {};
    //...
private:
    Container storage;
};

Store<std::map<int, float>> myStore; 
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • Instead of `value_type`, one could also use the result-type of `operator[]`, that is `using value_type = decltype(std::declval().operator[](size_t{}))`. This possibly reduces the constraints on `Container`. – davidhigh Jun 22 '15 at 11:09
  • @davidhigh all standard containers have `value_type`, but not all have `operator[]`. – Anton Savin Jun 22 '15 at 12:34
1

As an alternative to the answer by @TartanLlama, you can also use an alias for deque.

template<typename T>
using deque_alias = deque<T>;

int main(int argc, char *argv[])
{
    Store<int,deque_alias> myStore;   // now it will compile!
}

Then also the default template parameters (here std::allocator<T>) are used correctly. (Some compilers like MSVC have problems with that and will fail otherwise, as they expect two template parameters, see here for example).

Community
  • 1
  • 1
davidhigh
  • 14,652
  • 2
  • 44
  • 75