Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you use C++ templates to specify a collection type and the specialization of that type?

Example, I want to specialize a class to have a member variable that is an stl container, say a vector or a list, so I need something like:

template <class CollectionType, class ItemType>
class Test
{
public:
    CollectionType<ItemType> m_collection;
};

So I can do:

Test  t = Test<vector, int>();
t.m_collection<vector<int>> = vector<int>();

But this generates

test.cpp:12: error: `CollectionType' is not a template
like image 485
justinhj Avatar asked Jun 05 '09 15:06

justinhj


2 Answers

What you want is a template template parameter:

template <template <typename> class CollectionType, class ItemType>
class Test
{
public:
    CollectionType<ItemType> m_collection;
};

What we did here is specifying that the first template parameter, i.e. CollectionType, is a type template. Therefore, Test can only be instantiated with a type that is itself a template.

However, as @Binary Worrier pointed in the comments, this won't work with STL containers since they have 2 template parameters: one for the elements type, the other one for the type of the allocator used for managing storage allocation (which has a default value).

Consequently, you need to change the first template parameter so that it has two parameters:

template <template <typename,typename> class CollectionType, class ItemType>
class Test
{
public:
    CollectionType<ItemType> m_collection;
};

But wait, that won't work either! Indeed, CollectionType awaits another parameter, the allocator... So now you have two solutions:

1 . Enforce the use of a particular allocator:

CollectionType<ItemType, std::allocator<ItemType> > m_collection

2 . Add a template parameter for the allocator to your class:

template <template <typename,typename> class CollectionType, 
          class ItemType,
          class Allocator = std::allocator<ItemType> >
class Test
{
public:
    CollectionType<ItemType, Allocator> m_collection;
};

So as you see, you end up with something rather complicated, which seems really twisted to deal with STL containers...

My advice: see Greg Rogers' answer for a better approach :)!

like image 108
Luc Touraille Avatar answered Sep 25 '22 14:09

Luc Touraille


Why not do it like this?

template <class CollectionType>
class Test
{
public:
    CollectionType m_collection;
};

Test  t = Test<vector<int> >();
t.m_collection = vector<int>();

If you need the itemtype you can use CollectionType::value_type.

EDIT: in response to your question about creating a member function returning the value_type, you do it like this:

typename CollectionType::value_type foo();

You add the typename because CollectionType has not been bound to an actual type yet. So there isn't a value_type it could look up.

like image 23
Greg Rogers Avatar answered Sep 23 '22 14:09

Greg Rogers