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.
Related
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.
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> > ;
Every allocator class must have an interface similar to the following:
template<class T>
class allocator
{
...
template<class Other>
struct rebind { typedef allocator<Other> other; };
};
And classes that use allocators do something redundant like this:
template<class T, class Alloc = std::allocator<T> >
class vector { ... };
But why is this necessary?
In other words, couldn't they have just said:
template<class T>
class allocator { ... };
template<class T, template<class> class Alloc = std::allocator>
class vector { ... };
which is both more elegant, less redundant, and (in some similar situations) potentially safer?
Why did they go the rebind route, which also causes more redundancy (i.e. you have to say T twice)?
(Similar question goes to char_traits and the rest... although they don't all have rebind, they could still benefit from template template parameters.)
Edit:
But this won't work if you need more than 1 template parameter!
Actually, it works very well!
template<unsigned int PoolSize>
struct pool
{
template<class T>
struct allocator
{
T pool[PoolSize];
...
};
};
Now if vector was only defined this way:
template<class T, template<class> class Alloc>
class vector { ... };
Then you could just say:
typedef vector<int, pool<1>::allocator> int_vector;
And it would work perfectly well, without needing you to (redundantly) say int twice.
And a rebind operation inside vector would just become Alloc<Other> instead of Alloc::template rebind<Other>::other.
A quoted text from Foundations of Algorithms in C++11, Volume 1, chap 4, p. 35 :
template <typename T>
struct allocator
{
template <typename U>
using rebind = allocator<U>;
};
sample usage :
allocator<int>::rebind<char> x;
In The C++ Programming Language, 4th edition, section 34.4.1, p. 998, commenting the 'classical' rebind member in default allocator class :
template<typename U>
struct rebind { using other = allocator<U>;};
Bjarne Stroustrup writes this:
The curious rebind template is an archaic alias. It should have been:
template<typename U>
using other = allocator<U>;
However, allocator was defined before such aliases were supported by C++.
But why is this necessary?
What if your allocator class has more than one template argument?
That's pretty much it in terms of why it is generally discouraged to use template template arguments, in favor of using normal template arguments, even if it means a bit of redundancy at the instantiation site. In many cases (however, probably not for allocators), that argument might not always be a class template (e.g., a normal class with template member functions).
You might find it convenient (within the implementation of the container class) to use a template template parameter just because it simplifies some of the internal syntax. However, if the user has a multi-argument class template as an allocator he wants to use, but you require the user to provide an allocator which is a single-argument class template, you will in effect force him to create a wrapper for almost any new context in which he must use that allocator. This not only unscalable, it can also become very inconvenient to do. And, at this point, that solution is far from being the "elegant and less redundant" solution you originally thought it would be. Say you had an allocator with two arguments, which of the following is the easiest for the user?
std::vector<T, my_allocator<T,Arg2> > v1;
std::vector<T, my_allocator_wrapper<Arg2>::template type > v2;
You basically force the user to construct a lot of useless things (wrappers, template aliases, etc.) just to satisfy your implementation's demands. Requiring the author of a custom allocator class to supply a nested rebind template (which is just a trivial template alias) is far easier than all the contortions you require with the alternative approach.
In your approach you are forcing the allocator to be a template with a single parameter, which might not be always the case. In many cases, allocators can be non-template, and the nested rebind can return the same type of the allocator. In other cases the allocator can have extra template arguments. This second case is the case of std::allocator<> which as all templates in the standard library is allowed to have extra template arguments as long as the implementation provides default values. Also note that the existence of rebind is optional in some cases, where allocator_traits can be used to obtain the rebound type.
The standard actually mentions that the nested rebind is actually just a templated typedef:
§17.6.3.5/3
Note A: The member class template rebind in the table above is
effectively a typedef template. [ Note: In general, if the name
Allocator is bound to SomeAllocator<T>, then
Allocator::rebind<U>::other is the same type as SomeAllocator<U>,
where someAllocator<T>::value_type is T and SomeAllocator<U>::value_type is U. — end note ] If Allocator is a class template
instantiation of the form SomeAllocator<T, Args>, where Args is zero
or more type arguments, and Allocator does not supply a rebind member
template, the standard allocator_traits template uses SomeAllocator<U, Args> in place of Allocator:: rebind<U>::other by default. For
allocator types that are not template instantiations of the above
form, no default is provided.
Suppose you want to write a function taking all sorts of vectors.
Then it is much more convenient being able to write
template <class T, class A>
void f (std::vector <T, A> vec) {
// ...
}
than having to write
template <class T, template <class> class A>
void f (std::vector <T, A> vec) {
// ...
}
In most of the cases, such a function does not care about the allocator anyway.
Further note that allocators are not required to be a template. You could write separate classes for particular types that need to be allocated.
An even more convenient way of designing allocators would probably have been
struct MyAllocator {
template <class T>
class Core {
// allocator code here
};
};
Then it would have been possible to write
std::vector <int, MyAllocator> vec;
rather than the somewhat misleading expression
std::vector <int, MyAllocator<int> > vec;
I am not sure whether the above MyAllocator is permitted to be used as an allocator after adding a rebind, i.e. whether the following is a valid allocator class:
struct MyAllocator {
template <class T>
class Core {
// allocator code here
};
template <class T>
struct rebind { using other=Core<T>; };
};
in my work we do a lot of pair programming, and i wrote a function that ONLY accepts containers of a SINGLE type or its derivates, but my co-worker is afraid it will fail code review because it looks so damn ugly and says there's gotta be a better way:
here is the signature, the Fruit class is a base class that i renamed Fruit just for this thread:
template <class Container>
typename enable_if<is_base_of<Fruit, typename remove_pointer<typename Container::value_type>::type>::value, void>::type
processFruits(container& fruits)
{
//process the elements and change them if needed. thats why no const
}
what it does is: returns void and enables the function IF its a container AND the type inside the container is a Fruit and/or derivided of fruit. i also used std::remove_pointer because i needed to know the "type" of the pointer (the container will most likely have pointers).
this compiles and works as intended, but as i said i don't know it its the best way to do it, it seems too verbose and might get cut of on code review.
EDIT: this also accepts templated classes, don't have to be containers. is there a way i can limit it to only accept STL containers?
any alternate ideas or is it fine like it is?
thanks in advance.
It is a bit horrible to read.
Well for starters you don't need to say enable_if<B, void> you can just say enable_if<B> and use the default template argument.
You can easily split it into separate pieces:
template <class T>
struct is_fruity
: is_base_of<Fruit, T>
{ };
template <class Container, typename Value = typename Container::value_type>
struct is_fruit_container
: is_fruity<typename remove_pointer<Value>::type>>
{ };
template<class Container>
typename enable_if<is_fruit_container<Container>::value>::type
processFruits(Container& fruits)
{
//process the elements and change them if needed. thats why no const
}
If you have a compiler supporting alias templates you can make it even easier to read:
template<typename Cond>
using Require = typename enable_if<Cond::value>::type;
template<class Container>
Require<is_fruit_container<Container>>
processFruits(Container& fruits)
{
//process the elements and change them if needed. thats why no const
}
this also accepts templated classes, don't have to be containers. is there a way i can limit it to only accept STL containers?
I'm not sure what you mean by "templated classes", it only accepts types with a nested value_type type which is a type derived from Fruit or a pointer to such type, it doesn't have to be a template. To limit it to "STL containers" you need to write a trait to indentify an "STL container", however you want to define that. To do it properly you'd need a trait that tests for begin(), end(), size() members, as well as all the nested types specified by the container requirements, iterator, value_type, etc.
template<typename C, typename Chead, typename... Ctail>
struct static_and
{
static const bool value = C::value && static_and<Chead, Ctail...>::value;
};
template<typename C>
struct static_and<C>
{
static const bool value = C::value;
};
template<typename C>
struct is_container
: static_and<has_begin<C>, has_end<C>, has_iterator<C>, has_value_type<C> /* etc */>
{ };
Normally in templates you want to know the entire type, but in my case I need to know more, and want to "break up" the type. Take this example:
template <typename Collection<typename T> >
T get_front(Collection const& c)
{
return c.front();
}
How can I achieve that? Note: I need it to to automatically deduce the types, not pass in something like <std::vector<int>, int>
Edit: A C++0x way can be found at the end.
Edit 2: I'm stupid, and a way shorter C++98/03 way than all this traits stuff can be found at the end of the answer.
If you want your function to work for any arbitary standard library container, you need to pull out some Template Guns.
The thing is, that the different container take a different amount of template parameters. std::vector, std::deque and std::list for example take 2: the underlying item type T and the allocator type Alloc. std::set and std::map on the other hand take 3 and 4 respectively: both have the key type K, map takes another value type V, then both take a comparator Compare type and the allocator type Alloc. You can get an overview of all container types supplied by the standard library for example here.
Now, for the Template Guns. We will be using a partially specialized traits metastruct to get the underlying item type. (I use class instead of typename out of pure preference.)
template<class T>
struct ContainerTraits;
// vector, deque, list and even stack and queue (2 template parameters)
template<
template<class, class> class Container,
class T, class Other
>
struct ContainerTraits< Container<T,Other> >{
typedef T value_type;
};
// for set, multiset, and priority_queue (3 template parameters)
template<
template<class, class, class> class Container,
class T, class Other1, class Other2
>
struct ContainerTraits< Container<T,Other1,Other2> >{
typedef T value_type;
};
// for map and multimap (4 template parameters)
template<
template<class, class, class, class> class Container,
class Key, class T, class Other1, class Other2
>
struct ContainerTraits< Container<Key,T,Other1,Other2> >{
typedef Container<Key,T,Other1,Other2> ContainerT;
// and the map returns pair<const Key,T> from the begin() function
typedef typename ContainerT::value_type value_type;
};
Now that the preparation is done, on to the get_front function!
template<class Container>
typename ContainerTraits<Container>::value_type
get_front(Container const& c){
// begin() is the only shared access function
// to the first element for all standard container (except std::bitset)
return *c.begin();
}
Phew! And that's it! A full example can be seen on Ideone. Of course it would be possible to refine that even further, to the point of returning the actual value that is mapped to a key in a std::map, or use container specific access functions, but I was just a bit too lazy to do that. :P
Edit
A way easier C++0x way is using the new trailing-return-type function syntax, of which an example can be found here on Ideone.
Edit 2
Well, I don't know why, but I totally didn't think of the nested typedefs when writing this answer. I'll let the verbose way stay as a reference for traits classes / pattern matching a template. This is the way to do it, it is basically the same I did with the traits classes, but ultimately less verbose.
You could do this if you know it's not an associative container.
template <typename Collection>
Collection::type_name get_front(Collection const& c)
{
return c.front();
}
I'm assuming you want both Collection and T as template parameters. To do that simply type
template< template < typename > class Collection, typename T >
T get_front( Collection< T > const& c )
...
The construct template < typename > class Collection tells the compiler that Collection is a template itself with one parameter.
Edit:
As pointed out be Xeo, vector takes two template parameters, and your templates need to reflect that, i.e.
template< template < typename, typename > class Collection,
typename T, typename Alloc >
T get_front( Collection< T, Alloc > const& c )
...
Seeing as Collection<T> is known ahead of time, I think what you want is:
template <typename T>
T get_front(Collection<T> const& c)
{
return c.front();
}
The only part that's changing is what T is, it's always in a Collection (contents, not the container) so you don't have put that as part of the template.
If the container was changing, using c.front() could be dangerous. You would need to verify that the collection type had a method front that took no parameters and return a T.
Edit:
If you do need to template Collection, then that's more like:
template<typename C, typename T>
T get_front(C<T> const & c)
I would avoid something that generic if you can, perhaps specializing the function for collections you know will be used, or to a particular class of classes (if possible).