partial specialize template with template argument - c++

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;

Related

Templated Map/Dictionary Implementation

This is somewhat similar to a previous question of mine, I'm trying to implement a templated Map class.
template<typename T, size_t M> class Array { ... } // Fixed-size container
template<typename T> class Vector { ... } // Variable-size container
Method 1:
template<typename KeyContainer, typename ValueContainer> class Map
{
KeyContainer keys;
ValueContainer values;
}
The problem here is I cannot guarantee that both containers will be the same, the user may pass an Array<T, M> as the key container and a Vector<T> as the value container leading to problems once the Map starts to exceed the size specified in the Array<T, M> template.
Method 2:
template<typename KeyType, typename ValueType, template<typename> class Container> class Map
{
Container<KeyType> keys;
Container<ValueType> values;
// What if Container is an Array<T, M>?
}
but as demonstrated in my previous question that I linked it seems it is impossible to do so: receive a container template that has varying parameter count, unless there's some clever template tricks I'm not aware of.
Method 3:
Implement Method 1 and just put a note in the documentation that tells the user that KeyContainer and ValueContainer must be the same type of container.
Question:
What would be the most optimal way to go about this problem?
Method 2 is a good solution, as you users can always "bind" template parameters by using template aliases:
template <typename T>
using ArrayOf5 = Array<T, 5>;
int main()
{
Map<int, float, ArrayOf5> m;
}
live wandbox example
You could allow Container to take more template parameters by using a variadic pack:
template <typename...> class Container
But then it wouldn't work for non-type template parameters as in the case of Array.
You could store data as:
template <class Key, class Value, template<typename> class Container>
class Map
{
Container<std::pair<Key, Value> > store;
};
Disclaimer: Personally I don't think this makes much sense. I don't see a use case for changing the Container used in a Map ever. It is also very hard for any user of your Map to see all requirement for a Container (eg. what methods does a Container need? Which may throw? Which are const?) In any reasonable scenario, users will use some default Container of yours. In any case the additional template argument won't see much use which is why I'd stick to a specific Container type.

Templated Fixed + Variable-Sized Class

Let's say I have several container classes like these:
template<typename T> class Container
{
/* ... */
};
template<typename T, size_t> class Array : public Container<T>
{
/* Fixed-sized Container */
};
template<typename T> class Vector : public Container<T>
{
/* Variable-sized Container */
};
And I have a class which accepts one of these as a template parameter:
template<typename T, template<typename> class U, size_t M> class Polygon
{
U<T> Vertices; // Problem, what if user passes Array (it needs 2 parameters)
U<T, M> Vertices; // Problem, what if the user wants to use a variable-sized container (it needs only 1 parameter)
};
My question is, can I somehow (probably through tricky template parameter magic) make the consuming class accept any type of container (fixed or variable-sized, even with differing template signatures)?
The only guarantees about the template signatures are, if it is a Fixed-sized container it will have 2 parameters <Type, Size> and one if it is a Variable-sized container <Type>
It's way less tricky than you think it is. You can just template on the container itself:
template <class Container>
class Polygon {
Container vertices;
};
This will work for anything that meets your container requirements, be it fixed sized or not.
The problem of choosing the right template arguments for the container gets moved to instantiation point where the parameters and types must be known anyways.

Template template parameters - extension to std::array

I have a question about template template parameters:
Let's consider the following class:
template<typename T, template<class, class=std::allocator<T> > class UnderlyingContainerType = std::vector>
class MyContainer
{
public:
T Value1;
T Value2;
UnderlyingContainerType<T> Container;
MyContainer() {}
/* methods implementation goes here */
};
In this example, MyContainer is a container chat uses an underlying STL-compatible container to do whatever stuff.
Declaring the underlying container type as a template template parameters, instead of a regular template argument, allows handy usage of the class MyContainer, such as:
MyContainer<int> MCV; //implicitly using vector
MyContainer<int, std::list> MCL; //no need to write std::list<int> thanks to the
//template template parameters
Now, while this would work perfectly with most of the STL-container, such as std::vector, std::deque, std::list, and so forth, it would not work, for example, with std::array provided in c++11.
The reason is that std::array has a different signature of template parameters than vector. Specifically, they are:
std::vector<class T, class allocator = std::allocator<T> >
std::array<class T, int N>
MY question is if there is a way to generalize the class MyContainer, so that the underlying container can be std::array as well.
I thank you in advance.
The commonality between the interfaces of vector and array are limited to the element type. Your container should reflect that:
template<typename T, template<typename> class underlying_container>
struct container
{
underlying_container<T> storage;
};
Usage now requires a tiny trick:
template<typename T> using vec = vector<T>;
template<typename T> using arr2 = array<T, 2>;
Note that vec, unlike vector, has a fixed allocator and that arr2, unlike array, has a fixed size.
Usage is now simple:
container<int, vec> a;
container<double, arr2> b;
See the example in action.
Alternatively, if you prefer to match up the interface to that used by vector, just add a template alias for array that instantiates the size and adds an unused type parameter:
template<typename T, typename> using arr2 = array<T, 2>;
See it in action.
I don't know how to achieve exactly, what you want. But if you don't require the ability to write MyContainer<int> MCV; you could use
template<class UnderlyingContainerType, class T = typename UnderlyingContainerType::value_type>
class MyContainer
{
public:
T Value1;
T Value2;
UnderlyingContainerType Container;
MyContainer() {}
/* methods implementation goes here */
};
int main() {
MyContainer<std::vector<int>> MCV{};
MyContainer<std::array<int, 5>> MCA{};
}
Which is not more to type than MyContainer<int, std::vector> MCV;
Also you can of course still add an alias for your vector based version:
template<class T>
using MyContainerV = MyContainer < std::vector<T> > ;

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>.

How to implement is_stl_vector

I want to specialize a template for STL's vector template arguments. Something like this:
// (1)
template <typename T>
class A
{
...
};
// (2)
template <>
class A<std::vector<> >
{
...
};
I don't care what is the type of the vector element. I would like to use it as follows:
A<int> a1; // Will use the general specialization
A<std::vector<int> > a2; // Will use the second specialization
In general I've been trying to define something similar to boost's type traits. Something like
template <class T>
struct is_stl_vector
{
// Will be true if T is a vector, false otherwise
static const bool value = ...;
};
I cannot use template template (I think so) because it should compile for non-template types too. Is it possible at all?
You can simply specialize like this:
// (2)
template <typename T, typename Alloc>
struct A<std::vector<T, Alloc> >
{...};
The specialization goes like this:
// (2)
template <class T, class U>
class A<std::vector<T, U> >
{
...
};
Note that it is not guaranteed to work (and there si no other way that's guaranteed to work), because the template parameter count of std::vector may vary across implementations. In C++0x, this should be solvable using parameter packs.