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.
Related
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.
For the moment, I have a class, of the following form:
template <std::size_t N,
class T,
class Allocator = typename std::conditional<N, void, std::allocator<T>>::type>
class myclass {};
Which is a particular container with the following behaviour:
if N > 0, then the container has a static size of N, and the Allocator template parameter should be void.
if N == 0, then the container is of dynamic size of, and the Allocator parameter will be used.
But I am not satisfied with this design because it does not seem elegant. I would like a solution standard-like or boost-ready. Maybe such a problem has already been encountered for the design of one of the boost libary. If so what solution has been chosen?
Considering the fact that I want to keep a single version of myclass, and not two version static_myclass and dynamic_myclass.
This might be a good use-case for CRTP. Have a base class which does all the important stuff, which asks its derived class for the actual objects:
template <typename Derived, typename T>
class myclass_base_impl {
// generic stuff
// static_cast to Derived& to get actual data
};
Then, you have two versions of it. The dynamic one:
template <typename T>
class myclass_dynamic
: public myclass_base_impl<myclass_dynamic<T>, T>
{
/* stuff that uses std::allocator<T> */
};
And the static one:
template <typename T, size_t N>
class myclass_static
: public myclass_base_impl<myclass_static<T, N>, T>
{
// presumably something like
T data_[N];
};
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 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> > ;
I am pondering about partial specialization. While I understand the idea, I haven't seen any real-world usage of this technique. Full specialization is used in many places in STL so I don't have a problem with that. Could you educate me about a real-world example where partial specialization is used? If the example is in STL that would be superior!
C++0x comes with unique_ptr which is a replacement for auto_ptr which is going to be deprecated.
If you use unique_ptr with an array type, it uses delete[] to free it, and to provide operator[] etc. If you use it with a non-array type, it uses delete. This needs partial template specialization like
template<typename T>
struct my_unique_ptr { ... };
template<typename T>
struct my_unique_ptr<T[]> { ... };
Another use (although a very questionable) is std::vector<bool, Allocator> in the standard library. The bool specialization uses a space optimization to pack bools into individual bits
template<typename T, typename Allocator = std::allocator<T> >
struct vector { ... };
template<typename Allocator>
struct vector<bool, Allocator> { ... };
Yet another use is with std::iterator_traits<T>. Iterators are required to define the nested typedefs value_type, reference and others to the correct types (for a const iterator, reference would usually be T const&, for example) so algorithms may use them for their work. The primary template uses type-members of the iterator type in turn
template<typename T>
struct iterator_traits {
typedef typename T::value_type value_type;
...
};
For pointers, that of course doesn't work. There is a partial specialization for them
template<typename T>
struct iterator_traits<T*> {
typedef T value_type;
...
};
In some stl implementations collections like std::vector and std::list use partial template specialization to reduce the amount of code generated for collections of pointers.
Each instantiation of a template for a type T creates new code. However pointer types are effectively all the same so generating new code for every type is a waste. This can be reduced by implementing the private part of pointer collections with void pointers and then casting these to the appropriate type in the public interface. This greatly reduces the code generated for pointer collections.
I think this is covered in Effective STL.
Taken from MSDN (Partial Specialization of Class Templates (C++))
// partial_specialization_of_class_templates.cpp
template <class T> struct PTS {
enum {
IsPointer = 0,
IsPointerToDataMember = 0
};
};
template <class T> struct PTS<T*> {
enum {
IsPointer = 1,
IsPointerToDataMember = 0
};
};
template <class T, class U> struct PTS<T U::*> {
enum {
IsPointer = 0,
IsPointerToDataMember = 1
};
};