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>.
Related
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.
I have a template function that takes a container as a parameter
(I use this function for both vector, set, map so trying to avoid it would cost a lot of code copying)
so naturally I declared it as:
template<template<class T, class Allocator = std::allocator<T>> class
Container> Container<std::weak_ptr<A>>* Foo() {...}
Note that the return value of Foo uses the default parameter of the container.
When I use the function with vector, list or set it works just fine;
the problem I have is when I try to use this template with a map container with the comparator and value type fixed (which is defined as MapToValue<ValueType>::FromKey with A and Comparator<Key> already defined):
template<class Value> class MapToValue {
template<class Key, class Allocator = std::allocator<std::pair<const
Key, Value>>> FromKey : public std::map<Key, Value, Comparator<Key>,
Allocator> {...}
In this case the default value of the allocator is diffrent then the one in the definition of Foo - std::allocator<std::pair<_Ty, std::string>> vs. std::allocator<_Ty>.
Long story short, I need that to send to Foo a container with a second parameter that can be defaulted WITHOUT knowing what this default type would be (so this function template can be used for both map, vector or basically any other container). Is this possible?
EDIT: I cannot use C++11 in any way, the compiler is gcc 4.1.2 (out of my control)
In c++11 you can take any template as template argument:
template<template <class ...> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
There's a misunderstanding here of how template template parameters operate. This declaration:
template<template<class T, class Allocator = std::allocator<T>> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
Is just an overly verbose version of this declaration:
template < template <class, class> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
(What is A btw?)
It doesn't matter what the names or defaults of the types are that Container takes - Foo is simply templated on some class template that takes two template type arguments. That works with vector because it is a class template that takes two template type arguments:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
It does not work with map because that one takes four template type arguments:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
In C++11, you can generalize your function to have a template template argument take an arbitrary number of template type arguments:
template < template <class...> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
Though, however, this is never going to work as some container types can't be constructed with only a single template argument (e.g. map).
I found a solution, it isn't elegant but it works:
Sending to Foo another template argument that would be the way to deafault the Allocator:
template<template<typename> class DefaultAllocator, template<typename T,
typename Allocator = DefaultAllocator<T>> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
This way I send Foo<std::allocator,std::vector> all the same.
Also by creating some wrapper template<class T> class MapAllocatorWrapper : std::allocator<std::pair<T,std::string>>, I can send Foo<MapAllocatorWrapper, MapToType<std::string>::FromKey after slight adjustments.
I have this template
template <typename T, size_t maxsiz = 6>
class Array;
and i have this adaptor template
template <typename T, template <typename> class Container = std::vector>
class Stack;
What i want to do is use Array with Stack like so
Stack<int, Array> s;
However, the default constructor of Array does not fulfill the requirements made by Stack, so i need to specialize Stack for Array.
Ideally, i only want to specialize the ctor of Stack<T, Array>, so i can give the inner Array member the right argument at initialization.
i have tried this
template <typename T>
class Stack<T, Array>::Stack() : container(0) { } // container is the name of the wrapped Container
it, however, has problems. First, it wont compile (yeah...), and second, i want to be able to give the Array a different size, something like this
Stack<int, Array<int, 13>> stack;
or anything functionally equivalent (compile time constant for the size of the array). How would one accomplish this?
update
So i've done a bit more digging, and apparently you can't partially specialize a member function without a corresponding partial specialization of the entire class. That explains why my attempt will not work.
Rather than change Stack to work with Array, you should provide an adapter for Array that works with Stack:
template <typename T>
struct StackArray : Array<T> {
StackArray()
: Array<T>(0)
{ }
// anything else that needs to change here
};
That will let you do:
Stack<int, StackArray> s;
Also, your Stack template is incorrect. You cannot use std::vector as the default template for template <typename> class Container as std::vector takes two template arguments. It would probably be more useful if you made it:
template <typename T, typename Container = std::vector<T>>
struct Stack { .. };
As then we could add the maxsiz argument to StackArray as well and do something like:
Stack<int, StackArray<int, 13>> s;
This is how std::priority_queue and std::stack are designed.
Just always require an explicit array size and use something like:
template <typename T, int N>
class Stack<T, Array<T, N>>::Stack() : Array<T, N> { }
Usage:
Stack<int, Array<int>> x;
Stack<int, Array<int, 2>> y;
I'm just starting learning C++11 and I never saw this syntax in the list of new features:
template <template <typename> class F>
struct fun;
what is it and how does it work?
Note: What you are looking at is an "old" feature, and has been there since long before c++11.
template <template <typename> class F> struct Obj;
In the above Obj is a template only accepting a template-parameter which is also a template[1]; this is most often referred to as a template-template parameter [2].
1) in this specific example it will only accept a template that takes one type-parameter.2) Link to SO question: Template Template Parameters
Imagine that you'd like to have a wrapper around some class template; you don't care which class template this is as long as you can specify a template argument for it.
If so, you can use a template-template parameter as in the below example:
template<template<typename T> class TemplateType>
struct Obj {
TemplateType< int> m1;
TemplateType<float> m2;
};
template<typename T>
struct SomeTemplate { /* ... */ };
Obj<SomeTemplate> foo;
In the above, foo will be a Obj<SomeTemplate> having two members:
SomeTemplate< int> m1
SomeTemplate<float> m2
This should works in C++98 as well. This is a template as an argument from a template. I mean a template class will expected as the argument for F.
Maybe this page will help you: http://www.cprogramming.com/tutorial/templates.html
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> {
};
};