Template template collection accepting another template argument - c++

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.

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.

Related

How to test if a type parameter is a template type

Say I have a template class like so:
template < typename TParam >
class Test
{
// content
};
I want to pull out the first template parameter of TParam if it's a specialization of a class template. Something like:
template < typename TParam >
class Test
{
using TParamInner = TemplateType<TParam>::Type;
// use TParamInner here
};
Additional info:
I have access to all of C++98.
I have access to a subset of C++11.
I would prefer to avoid the stdlib if possible (assume this is
because I'm using an embedded system for which no stdlib is available and/or because I am heavily memory-constrained)
You can get close with something like:
template <class >
struct first_template_param;
template <template <class...> class Z, class T, class... Ts>
struct first_template_param<Z<T, Ts...>> {
using type = T;
}
It won't handle std::array or any other class templates that take non-type template parameters. But it'll handle all the "normal" class templates. You can always then add extra specializations for all the ones you want:
template <class T, size_t N>
struct first_template_param<std::array<T,N>> {
using type = T;
}
Thanks to #Barry for spurring the solution along.
It's not a complete answer for all template types, but it works for templates where all parameters are types, which is a large number of the most useful templates.
template < typename Head, typename ... Tail >
struct split { using first = Head; };
template <class >
struct cls_template_info; // fails on non-templates
template <template <class...> class Z, class... Ts>
struct cls_template_info<Z<Ts...>>
{
using type = typename split<Ts...>::first; // typename used to disambiguate
};
This can then be used as using T = cls_template_info<std::vector<int>>::first;.
You can't. A template type is never carried up to runtime. You have to instantiate it (this leads to a complete new type), and the compiler then generates the needed code to make it appear as if you have defined specifically for the type parameters you specified. Indeed, in old compilers (this has been solved a lot of time ago) when you instantiate a generic type in several compilation units, that lead to several repetitions of the same code in the final program. But as I've said, this has been solved time ago.

Can I use std::vector as a template parameter or does it need to be std::vector<T>?

I know this is a simple question but I just could not find the answer.
I am trying to do something like this but instead of with std::vector ultimately I want it to be std::shared_ptr or std::weak_ptr:
template <int dim, class ChunkClass, class PtrClass>
class BaseChunkWindow : public IChunkWindow<BaseChunkWindow<dim, ChunkClass, PtrClass>, IChunk<ChunkClass>> {
public:
...
private:
PtrClass< IChunk<ChunkClass> > ptr; <-- compiler doesn't like this line, however IChunk<ChunkClass>* works
};
It depends on what you are passing it to, if the template you're trying to instantiate takes as a parameter a class template accepting 2 (or in c++11 a variadic number of) types then you can pass std::vector to that. In most cases however, templates require types as parameters and you cannot pass the class template std::vector.
template <class T>
struct gimme_a_type{};
template <template <class,class> class T>
struct gimme_a_template{};
gimme_a_type<std::vector> //<-- does not compile, expected a type, got a template
gimme_a_type<std::vector<int> > //<-- compiles, expected a type, got a type
gimme_a_template<std::vector> //<-- compiles, expected a template, got a template that has the correct signature
gimme_a_template<std::vector<int> > //<-- does not compile, expected a template, got a type
In response to your edit, there are difficulties to using class templates as template parameters. Matching the number of parameters exactly is actually difficult to do when you have default arguments in the class template you're trying to pass (std::vector in our case).
Notice that the example above required a class template that takes 2 types, not just one. This is because std::vector takes two parameters, the second is just defaulted to std::allocator<T> for us.
The following example demonstrates the issue:
template <template <class, class> class Tem>
struct A
{
Tem<int> v; //<-- fails to compile on gcc, Tem takes two parameters
Tem<int, std::allocator<int> >; //<-- compiles, but requires a priori knowledge of Tem
};
template <template <class...> class Tem>
struct A2
{
Tem<int> v; //<-- This C++11 example will work, but still isn't perfect.
};
The C++11 example is better, but if someone passed a class that has as a signature template <class, bool = false> class A3 it fails again because A2 requires a class template that takes types and not a mix of types and non-types (false being the non-type template parameter in this example). So even though A3<int> would be a valid instantiation you couldn't pass that class to A2.
The solution there is to always use types in template parameter lists and use the std::integral_constant wrapper template to pass integral constants around.
There are a couple ways of doing it.
The limited way would be to use a template template parameter with just a limited number of parameters being passed, e.g. 3.
template<template<class,class,class> class Cont, class T, class V, class U>
void f(Cont<T,V,U>&& cont) {
//...
}
However that's pretty limiting and can be hard to manage if you decide to change it in the future.
So you can do it like so with the new Variadic Templates in C++11:
template<template<class...> class Cont, typename F, typename... Rest>
void f(Cont<F, Rest...>&& cont) {
//...
}
This would work on other containers or things and is probably much easier to manage.

not able to fully undertand how template template parameters work

I was reading Modern C++ design and not able to fully understand how template template parameter works.
For example as given in this article http://www.informit.com/articles/article.aspx?p=376878 we can create a Stack with template parameters of type and containers .
If we use just type and container as parameter to template Stack then it may create some issues like
template <typename T, class Cont>
class Stack;
template <typename> class List;
Stack<int, List<int> > aStack1; // OK
Stack<double, List<int> > aStack2; // legal, not OK
Stack<std::string, Deque<char *> > aStack3; // error!
In above code I can understand that aStack1 is fine,aStack2 and aStack3 are issues because if incompatible types between Stack element type and container element type.
As per article this can be resolved if we use template template parameter
template <typename T, template <typename> class Cont>
class Stack;
Stack<int,List> aStack1;
Stack<std::string,Deque> aStack2;
My doubt here is how can Deque knows that its element type is std::string or List element type is int???Is this done by template argument deduction?
Here we are creating a Deque with type T .
If we have defined stack as
template <typename T,template U, template <typename> class Cont>
class Stack;
then how can we instantiate Stack
Stack<std::string,int,Deque> // will this work????
My doubt here is how can Deque knows that its element type is std::string or List element type is int???Is this done by template argument deduction?
No, you explicitly instantiate the class template in the implementation of Stack which is likely to be implemented as:
template <typename T, template <typename> class Cont>
class Stack
{
Cont<T> _container; //here you use `T` as template argument to `Cont`
Cont<int> _ints; //you can do even this if you need a containter of int.
//though in this case you don't need this.
public:
void push(T const & data)
{
_container.push_back(data);
}
//etc
};
For a template to be instantinated its definition is required. In your examples, you only provide the template declaration, which is not enough for it to be instantinated. The definition gives the implementation which details how the template parameters are used. For example,
template <typename T, template <typename> class Cont>
class Stack : Cont<T>
{
typedef Cont<T> my_container;
public:
using my_container::push_back;
/* more details here */
};
Stack<int,List> aStack1;
Stack<std::string,Deque> aStack2;
implements aStack1 as derived from a List<int> and astack2 as derived from a Deque<std::string>.

Is there a way to bind a template<template> parameter?

Context
I have a custom comparator that takes another comparator and applies an additional check:
template <template <typename> class Comparator, typename T>
struct SoftOrder : public std::binary_function<T, T, bool> {
bool operator()(const T lhs, const T rhs) const {
return Comparator<T>()(lhs, rhs) && AnotherCheck();
}
};
I have a second class that accepts a comparator, e.g.:
template <template <typename> class Comparator>
class Processor { ... };
It is easy to instantiate a Processor with a standard comparator (e.g. std::less) like so:
Processor<std::less> processor1;
Processor<std::greater> processor2;
However it is not so easy to instantiate with SoftOrder as the compiler correctly complains about the missing second template argument:
Processor<SoftOrder<std::less> > processor3; // <-- Fails to compile
Current Solutions
I have come up with a few solutions prior to posting this question.
First Solution - Lots of Derived Classes
template <typename T>
struct SoftOrderLessThan : public SoftOrder<std::less, T> {};
template <typename T>
struct SoftOrderGreaterThan : public SoftOrder<std::greater, T> {};
The main drawback of this solution is the need to create a new struct every time a new variant is required, e.g.:
template <typename T>
struct SoftOrderLessThan : public SoftOrder<std::less, T> {}; // Never used after the next line.
Processor<SoftOrderLessThan> processor3;
Second Solution - A very specific bind class
template <template <typename> class Comparator>
struct BindToSoftOrder {
template <typename T>
struct type : public SoftOrder<Comparator, T> {};
};
This is slightly better in that we don't need to create the intermediate classes explicitly:
Processor<BindToSoftOrder<std::less>::type> processor3;
The downside is the requirement of a class specialised for this situation which cannot really be generalised by making SoftOrder a template parameter on BindToSoftOrder as this would make it a template<template<template>>> which is not permitted by the standard.
Third Solution - C++11 template aliases
template <typename T>
using SoftOrderLessThan = SoftOrder<std::less, T>;
Nicer than the first option in that it doesn't require the introduction of new classes, however still requires littering the code with this extra code that is only used in passing onwards to another template class:
template <typename T>
using SoftOrderLessThan = SoftOrder<std::less, T>; // Never used again
Processor<SoftOrderLessThan> processor3;
Finally, the question
Is there a generic way to bind my custom comparator to a specific comparator in the following manner?
Processor<SomeCoolMetaTemplateBind<SoftOrder, std::less>::type> processor3;
I believe if all of the template parameters were simple types I could just do something like Processor<boost::mpl::bind<SoftOrder, std::less> >, but the presence of the template type in the template parameter list prevents this from occurring.
An ideal solution would involve C++03, but am happy to hear C++11 solutions as well.
If it's not possible, I hope at least the question was interesting.
Seems like this would work:
template <
template <template <typename> class,class> class U,
template <typename> class X
>
struct SomeCoolMetaTemplateBind {
template <typename T>
struct type : public U<X,T> {
};
};

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
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 :)!
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.
Comeau online likes this:
#include <vector>
template <template <class T, class A = std::allocator<T> > class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
void foo()
{
using std::vector;
Test<vector,int> t = Test<vector, int>();
t.m_collection = vector<int>();
}