C++11 "late binding" of template arguments - c++

Please don't get my "late binding" wrong, I don't mean usual late binding at runtime, I mean something else and cannot find a better word for it:
Consider I am working on a container (or similar) data structure Containor for some value type V that needs to compare these values with a comparator, so my first template looks like this
template<typename Val, typename Comp = std::less<Val>>
struct Containor{};
Now, my Containor structure makes use of another container internally. Which container is to be used should be configurable by template arguments as well, lets say the default is std::set. So my next version of Containor looks like this:
template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set<Val,Comp>>
struct Containor{};
and here is where the code begins smelling IMHO. As long as the user is satisfied with the default implementation of the inner container, everything is fine. However, suppose he wants to use the new google btree set implementation btree::btree_set instead of std::set. Then he has to instanciate the template like this:
typedef Containor<int,std::less<int>,btree::btree_set<int,std::less<int>> MyContainor;
^^^^^^^^^^^^^^^^^^^
I have underlined the part where my problem lies. The CLIENT CODE has to instanciate the btree_set with the right parameters. This honestly sucks, because the Containor class always needs a set of exactly the same type and comparator as its own first two template arguments. The client can - by accident - insert other types here! In addition, the client has the burden of choosing the right parameters. This might be easy in this case, but it hard if the inner container must for example be a set of pairs of the value type and some other type. Then the client has an even harder time getting the type parameters of the inner set correct.
So what I want is a way in which the client code only hands in the raw template and the Containor internally instanciates it with the correct arguments, i.e. something like that:
template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set >
struct Containor{
typedef Cont<Val,Comp> innerSet;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ container instanciates the inner containor
};
typedef Containor<int,std::less<int>,btree::btree_set> MyContainor;
// ^^^^^^^^^^^^^^^^
// client only hands in raw template
Of course, this is no valid C++!
So I thought about ways to solve this problem. The only solution I could think of was writing "binder classes" for all data structures I want to use, like this:
struct btree_set_binder{
template<typename V, typename C = std::less<V>>
struct bind{
typedef btree::btree_set<V,C> type;
}
};
Now I can define my Containor with a set binder
template<typename Val, typename Comp = std::less<Val>, typename ContBinder = btree_set_binder >
struct Containor{
typedef btree_set_binder::bind<Val,Comp>::type innerSet;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ works like a charm
};
Now, the user must only supply the desired binder class and the Containor will instanciate it with the right arguments. So these binder classes would be okay for me, but it is quite a hassle writing binder classes for all containers. So is there a better or easier way to bind template arguments "late" in C++11, i.e., inside another template that retrieves the raw template as parameter.

Maybe make your own comparator trait.
// Comparator trait primary template
template <typename T> stuct MyComparator
{
typedef typename T::key_compare type;
};
// Comparator trait non-standard usage example
template <typename U, typename V, int N>
struct MyComparator<WeirdContainer<U, V, N>>
{
typedef std::greater<V> type;
};
template <typename T, typename Cont = std::set<T>>
struct MyAdaptor
{
typedef typename MyComparator<Cont>::type comparator_type;
typedef T value_type;
// ...
};
I've renamed your "Containor" to "MyAdaptor", since this sort of construction is usually called an "adaptor" class.
Usage:
MyAdaptor<int> a; // uses std::set<int> and std::less<int>
MyAdaptor<double, WeirdContainer<bool, double, 27>> b;
Update: In light of the discussion, you could even remove the outer type argument entirely:
template <typename Cont> struct MyBetterAdaptor
{
typedef MyAdaptorTraits<Cont>::value_type value_type;
typedef MyAdaptorTraits<Cont>::pred_type pred_type;
// ...
};
To be used like this:
MyBetterAdaptor<std::set<int>> c; // value type "int", predicate "std::less<int>"
Writing the MyAdaptorTraits template is left as an exercise.

So what I want is a way in which the client code only hands in the raw
template and the Containor internally instanciates it with the correct
arguments,
So what you need is obviously a template template parameter:
// std::set has three template parameters,
// we only want to expose two of them ...
template <typename V, typename C>
using set_with_defalloc = std::set<V,C>;
template<
typename Val,
typename Comp = std::less<Val>,
template <typename V, typename C> class Cont = set_with_defalloc>
struct Containor{
typedef Cont<Val,Comp> innerSet;
// ...
};

You should be able to do it with template template parameters, as in:
template<typename Val, typename Comp = std::less<Val>, template <typename...> class ContBinder = std::set>
struct Containor {
typedef ContBinder<Val, Comp> innerSet;
// ...
};
Note: you need the variadic typename... because std::set takes three template parameters (the third being an allocator) while other containers might not.

Related

C++ variadic template template argument that matches any kind of parameters

I was wondering if it's possible to write a template function that can take any other arbitrary template as a parameter and properly match the template name (i.e. not just the resulting class). What I know to work is this:
template<template<typename ...> class TemplateT, typename... TemplateP>
void f(const TemplateT<TemplateP...>& param);
Which will match for instance for f(std::vector<int>()) or f(std::list<int>()) but will not work for f(std::array<int, 3>()), as the second parameter is a size_t and no type.
Now I guess one could do something crazy like:
template<template<typename ...> class TemplateT, size... Sizes, typename... TemplateP>
void f(const TemplateT<Sizes..., TemplateP...>& param);
Hoping that the compiler would properly derive either the TemplateP ellipsis or the Sizes ellipsis to be empty. But not only is it ugly, it also will still just work for templates that take either types or size_t parameters. It still won't match arbitrary templates for instance with bool parameters.
Same goes for an approach with overloading:
template<template<typename ...> class TemplateT, typename... TemplateP>
void f(const TemplateT<TemplateP...>& param);
template<template<typename ...> class TemplateT, size... Sizes>
void f(const TemplateT<Sizes...>& param);
Furthermore, such approach wont' work if we would like to mix size_t and typenames. So what would be required to match anything would be something like this, where there are no constraints at all to what is allowed in the ellipsis:
template<template<...> class TemplateT, ... Anything>
void f(const TemplateT<Anything...>& param);
That syntax doesn't work but maybe there's other syntax to define something like this?
This is mainly me wondering what is possible in the language, thought there might actually be a use for it, if you have different templates where the first parameter is always fixed and you would like to change it based on the return type and keep everything else. Something like this:
template<
template<typename ValueT, ...> class TemplateT,
... Anything,
typename ValueT,
typename ResultT = decltype(some_operation_on_value_t(std::declval<ValueT>())>
TemplateT<ResultT, Anything...> f(const TemplateT<ValueT, Anything...>& in);
So, any way to make this work in a completely generic way using pattern matching?
This is not purely a thought experiment, as the use case for this where I was stuck was to create pure functional primitives that operate on containers and will implicitly construct immutable result containers. If the result container has a different data type we need to know the type the container operates on, so the only requirement on any container would be that the first parameter of the template needs to be the input type so it can be replaced with a different output type in the result, but the code should be oblivious to any template argument coming after that and should not care whether it's a type or a value.
Your interesting construct has two levels with variadic templates.
An outer variadic template parameter list TemplateP & Sizes for a function template
An inner parameter pack as the template parameters of your template template parameter TemplateT, a class template
First, let's look at the inner TemplateT class: why can the ellipsis operator not not match something like TemplateT< int, 2 >? Well, the standard defines variadic templates in §14.5.3 as
template<class ... Types> struct Tuple { };
template<T ...Values> struct Tuple2 { };
where the template argument pack in the first case may only match types and in the second version only values of type T. In particular,
Tuple < 0 > error; // error, 0 is not a type!
Tuple < T, 0 > error2; // T is a type, but zero is not!
Tuple2< T > error3; // error, T is not a value
Tuple2< T, 0 > error4; // error, T is not a value
are all malformed. Furthermore, it is not possible to fall back to something like
template<class ... Types, size_t ...Sizes> struct Tuple { };
because the standard states in §14.1.11:
If a template-parameter of a primary class template or alias template is a template parameter pack, it
shall be the last template-parameter. A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be deduced from the parameter-type-list
of the function template or has a default argument (14.8.2).
In other words, for class templates only one variadic parameter pack may appear in the definition. Therefore the above (double)-variadic class definition is malformed. Because the inner class always needs such a combination, it is impossible to write something as general as you conceived.
What can be rescued? For the outer function template, some shards can be put together, but you won't like it. As long as the second parameter pack can be deduced from the first, two parameter packs may appear (in a function template). Therefore, a function such as
template < typename... Args, size_t... N > void g(const std::array< Args, N > &...arr);
g(std::array< double, 3 >(), std::array< int, 5>());
is allowed, because the integer values can be deduced. Of course, this would have to be specialized for every container type and is far from what you had imagined.
You must have a metafunction that rebinds the type of container.
Because you cannot just replace first template parameter:
vector<int, allocator<int> > input;
vector<double, allocator<int> > just_replaced;
vector<double, allocator<double> > properly_rebound;
So, just write such a metafunction for known set of containers.
template<class Container, class NewValue> class rebinder;
// example for vectors with standard allocator
template<class V, class T> class rebinder< std::vector<V>, T > {
public:
typedef std::vector<T> type;
};
// example for lists with arbitrary allocator
template<class V, class A, class T> class rebinder< std::list<V,A>, T > {
typedef typename A::template rebind<T>::other AT; // rebind the allocator
public:
typedef std::list<T,AT> type; // rebind the list
};
// example for arrays
template<class V, size_t N> class rebinder< std::array<V,N>, T > {
public:
typedef std::array<T,N> type;
};
Rules of rebinding may vary for different containers.
Also you might require a metafunction that extracts value type from arbitrary container, not only std-conformant (typedef *unspecified* value_type)
template<class Container> class get_value_type {
public:
typedef typename Container::value_type type; // common implementation
};
template<class X> class get_value_type< YourTrickyContainer<X> > {
......
public:
typedef YZ type;
};
It would be awesome if we had such thing, as it would allow us to write a is_same_template trait in a breeze.
Until then, we specialize all the way.

template metaprogramming: (trait for?) dissecting a specified template into types T<T2,T3 N,T4, ...>

I'm trying to deduce the underlying template type T from a type E = T<T2,T3>. This would for example make it possible to make a template function pair_maker(const E & a) which can be used with one of several similar types of containers. Rough meta code:
template <typename T>
auto pairmaker(const E & a) -> PairContents<E,std::string>::type {
ContainerPairMaker<E,std::string>::type output;
... some code ...
return output;
}
PairContents<E,std::string>
would transform the type vector<int> into vector<pair(int,std::string)> or whatever<T1> into whatever<pair(T1,std::string)>.
Another similar example of type dissection is for std::array (or similar containers) where I like to figure out the container type to make a new similar array. For example for these kind of functions (this is actual working code now)
template <typename T >
auto make_some3(const T & a)
-> std::array<typename T::value_type,10*std::tuple_size<T>::value>{
return std::array<typename T::value_type,10*std::tuple_size<T>::value>{} ;
}
This works fine but what I'm after is to make the explicit use of 'std::array' automatic.
For std::array there's the tuple_size trait which helps, and a similar thing can be used to find the type for any second argument, but again I can't think of anything for finding the container type.
To summarize: what kind of machinery (if any) can be used for cases like these. To which extent is it possible to deal with mixes of template arguments, template-template arguments, any number of arguments, and non-template arguments of unknown types.
Here's an idea:
template <typename T, typename ...>
struct tmpl_rebind
{
typedef T type;
};
template <template <typename ...> class Tmpl, typename ...T, typename ...Args>
struct tmpl_rebind<Tmpl<T...>, Args...>
{
typedef Tmpl<Args...> type;
};
Usage:
typedef std::vector<int> IV;
typedef typename tmpl_rebind<IV, std::pair<double, std::string>>::type PV;
Now PV = std::vector<std::pair<double, std::string>>.
This is a self answer I came up with as a variant of the answer from Kerrek SB
It is possible to make a trait that extracts std::vector from std::vector<int> and exposes it as ::type via a trait. Yes, this solution is nearly identical to Kerrek's, but to me the use syntax is more aesthetic, putting the template parameters after ::type.
template <typename T, typename ...>
struct retemplate
{
typedef T type;
};
template <template <typename ...> class Tmpl, typename ...T>
struct retemplate<Tmpl<T...>>
{
template <typename ...AR>
using type=Tmpl<AR...> ;
};
with this you actually get retemplate<T<A,B,C>>::type equal to the template T
example use:
typedef std::vector<int> intvec;
typedef retemplate<intvec>::type<double> doublevec;
or to expose the container type
typedef std::vector<int> intv;
template <typename ...T>
using vector_T= retemplate<intv>::type<T...> ;
Note that when using this in template context, an extra template is required just after ::, like this: (elaborating on the comment from Xeo)
template <typename T>
typename retemplate<T>::template type<double> containedDouble(T& a) {
decltype(containedDouble(a)) out;
for (auto &i : a)
out.push_back(i);
return out;
}
What that does is to take an object of type T1<T2> and copy its content into a T1<double>. For example with T1==std::vector and T2==int.
I recommend taking a look at A. Alexandrescu's book Modern C++ Design.
If I recall correctly he explains how one might use type lists to store and access arbitrary types in a list-like fashion. These lists can be used to provide type information in a number of different situations. Take a look at the implementation of Loki to see how type lists can be utilized.
I'm not sure if this is helpful at all, but maybe you can learn something from the ideas used in Loki in order to solve or at least better understand your specific issues at hand.

Template argument deduction

I'm currently facing a problem I haven't been able to solve myself.
Basically what I'm trying to do is implement some linq-like behaviour in C++.
I'll start off with the code in my header:
template<typename T, template<class = T> class A,
template<class = T, template<class=T> class = A> class C>
class queryable
{
public:
typedef T value_type;
typedef A<value_type> allocator_type;
typedef C<value_type, allocator_type> container_type; // (1)
typedef queryable<T, A, C> type;
queryable(container_type const &) { }
template<typename _Out> queryable<_Out, A, C> select(/* some delegate */);
// more methods etc
}
And this is how I'd like it to be instantiated:
std::vector<int> my_vec;
queryable<std::vector<int> > q(my_vec);
Needless to say this doesn't work (otherwist I wouldn't be here :) )
Now the even stranger part is that even this doesn't seem to work:
std::vector<int> my_vec;
queryable<int, std::allocator, std::vector> q(my_vec);
As you can see (by looking at the select function), it is important to me to not just use something like this:
template<typename T> class queryable;
Any suggestions on how to solve this? And is this even possible?
Any help would be appreciated!
EDIT: the errors I'm getting:
../entry.cpp:19:58: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, template<class> class A, template<class, template<class> class<template-parameter-2-2> > class C> class failproof::collections::queryable’
../entry.cpp:19:58: error: expected a template of type ‘template<class, template<class> class<template-parameter-2-2> > class C’, got ‘template<class _Tp, class _Alloc> class std::vector’
../entry.cpp:19:61: error: invalid type in declaration before ‘;’ token
EDIT 2:
As far as I understand the compiler is complaining about C not taking 2 class arguments, but 1 class argument and 1 templated class argument (1), because I defined C to be that way.
Is there any way to resolve this issue?
There is a general method to 'explode' a type to test if it was created by a template, and to extract the types that were passed to that template. It is also possible to access the template itself and pass other parameters to it if you desire.
vector is a class template. When you apply parameters to it, you get something like vector<int>, which is a template class. A template class is a specific type, like any other type, it just happens to have been created via a class template.
The goal is, given a type T, to test if it is a template class, and if so to gain access to the class template that was used to create it, and also to access the parameters that were passed to the class template. In this sample, I just test for whether something is a one-arg or two-arg template, but the technique can easily be extended.
(Technically, vector is a two-arg template. There is a default for the second parameter, so vector<int> is actually vector<int, allocator<int> >, but it's still basically a two-arg template, not a one-arg template.)
The best place to start is with this sample code I've put on ideone. I'll copy the Exploder code at the end of this answer.
I begin with
typedef list<int> list_of_ints;
and proceed to use the Exploder template to access all the above information. For example, Exploder<list_of_ints> :: type_1 is the first parameter that was passed to the template, in this case int. The second parameter (this is the defaulted parameter) is allocator<int> and is accessible with Exploder<list_of_ints> :: type_2.
typedef Exploder<list_of_ints> :: type_2 should_be_an_allocator_int;
Given this second type, which we know was created by a template, we can access its parameter type, int, with Exploder< should_be_an_allocator_int > :: type_1, but it's more interesting to actually access the allocator template instead and pass a different parameter to it. This next line evaluates, in this example, to an allocator<double>.
typedef Exploder< should_be_an_allocator_int >
:: rebind<double> :: type should_be_an_allocator_double;
So, even if your list<...,...> type did not use the default allocator, you can access the allocator that was used, and also any class template that was used to create the allocator type.
Now that we have a suitable allocator, we can go back to our original template class list<int> and replace int with double:
Exploder<list_of_ints> :: rebind<double, should_be_an_allocator_double> :: type
To verify all this has worked, the sample code uses typeid(...).name() to print the actual type of the various objects, along with the correct type that it should be. You can see that they match.
(Also, some templates are such that their parameters are not types, but other class templates, or even other template templates. It should be possible to extract all that, but I'm not going to look into that here.)
(One last interesting technical note. Some types, such as allocator, have something called rebind to allow this sort of access. But the technique used above works for all template classes, even those without their own rebind)
The full code for the template Exploder
See sample code I've put on ideone for a full demo.
template <class>
struct Exploder;
template<class T, template<class> class Template>
struct Exploder< Template<T> > {
static const char * description() { return " One-arg template. Arg 1 is a type "; }
typedef T type_1;
template <class V>
struct rebind {
typedef Template<V> type;
};
};
template<class T, class U, template<class,class> class Template>
struct Exploder< Template<T,U> > {
static const char * description() { return " Two-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef T type_1;
typedef U type_2;
template <class V,class W>
struct rebind {
typedef Template<V,W> type;
};
};
template<class S, class T, class U, template<class,class,class> class Template>
struct Exploder< Template<S,T,U> > {
static const char * description() { return " Three-arg template. All args are types, as opposed to being (unapplied) templates. "; }
typedef S type_1;
typedef T type_2;
typedef U type_3;
};
The second template parameter of the standard containers (the allocator) is a type, not a template, so you need to change your third parameter to something like
template<typename, typename> class C
(Note that the default arguments in your template parameter specifications don't serve any purpose, so I omitted them here.)
Then you should be able to instantiate the template as
queryable<int, std::allocator, std::vector>
You may be better off just parametrising over the container type, and then using its value_type and allocator_type definitions:
template <typename C> class queryable
{
public:
typedef typename C::value_type value_type;
typedef typename C::allocator_type allocator_type;
typedef C container_type;
};
(One downside is that you can't directly access the allocator template; however, you can use the allocator type's nested rebind definition if you need to instantiate that template for other types.)
Also, typedef iterator const const_iterator; is wrong; that declares an unmodifiable iterator that can be used to modify the sequence, while const_iterator is supposed to be a modifiable iterator that can't be used to modify the sequence.
(A note on terminology. vector is a class template, i.e. without any parameters. And vector<int> is a template class, i.e. a class almost like any other except that it was created by a template.)
It is possible to use it as you wish, where queryable takes one template parameter:
queryable< vector<int> > q;
This means that querable is a template with just one parameter:
template <typename T>
struct queryable;
You then use a specialization that has more than one parameter:
template <typename ValueType, template<class T,class = allocator<T> > class ContainerTemplate>
struct queryable< ContainerTemplate<Contained> > {
typedef ValueType value_type;
typedef ContainerTemplate<ValueType> container_type;
typedef typename container_type :: allocator_type A;
// typedef ContainerTemplate <WhateverOtherValueTypeYouWish> ...
// typedef A :: rebind <SomeOtherType> ...
};
The last two lines, commented out, show how you can use ContainerTemplate as a class template, creating other types as required. ContainerTemplate is vector or list or set or something like that.
As #MikeSeymour has pointed out, the use of rebind might be the way to access the allocator class template.
Sample code on ideone

How to "pattern match" a template?

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

Default values in templates with template arguments ( C++ )

Assume I have a template (called ExampleTemplate) that takes two arguments: a container type (e.g. list, vector) and a contained type (e.g. float, bool, etc). Since containers are in fact templates, this template has a template param. This is what I had to write:
#include <vector>
#include <list>
using namespace std;
template < template <class,class> class C, typename T>
class ExampleTemplate {
C<T,allocator<T> > items;
public:
....
};
main()
{
ExampleTemplate<list,int> a;
ExampleTemplate<vector,float> b;
}
You may ask what is the "allocator" thing about. Well, Initially, I tried the obvious thing...
template < template <class> class C, typename T>
class ExampleTemplate {
C<T> items;
};
...but I unfortunately found out that the default argument of the allocator...
vector<T, Alloc>
list<T, Alloc>
etc
...had to be explicitely "reserved" in the template declaration.
This, as you can see, makes the code uglier, and forces me to reproduce the default values of the template arguments (in this case, the allocator).
Which is BAD.
EDIT: The question is not about the specific problem of containers - it is about "Default values in templates with template arguments", and the above is just an example. Answers depending on the knowledge that STL containers have a "::value_type" are not what I am after. Think of the generic problem: if I need to use a template argument C in a template ExampleTemplate, then in the body of ExampleTemplate, do I have to reproduce the default arguments of C when I use it? If I have to, doesn't that introduce unnecessary repetition and other problems (in this case, where C is an STL container, portability issues - e.g. "allocator" )?
Perhaps you'd prefer this:
#include <vector>
#include <list>
using namespace std;
template <class Container>
class ForExamplePurposes {
typedef typename Container::value_type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
}
This uses "static duck typing". It is also a bit more flexible as it doesn't force the Container type to support STL's Allocator concept.
Perhaps using the type traits idiom can give you a way out:
#include <vector>
#include <list>
using namespace std;
struct MyFunkyContainer
{
typedef int funky_type;
// ... rest of custom container declaration
};
// General case assumes STL-compatible container
template <class Container>
struct ValueTypeOf
{
typedef typename Container::value_type type;
};
// Specialization for MyFunkyContainer
template <>
struct ValueTypeOf<MyFunkyContainer>
{
typedef MyFunkyContainer::funky_type type;
};
template <class Container>
class ForExamplePurposes {
typedef typename ValueTypeOf<Container>::type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
ForExamplePurposes< MyFunkyContainer > c;
}
Someone who wants to use ForExamplePurposes with a non-STL-compliant container would need to specialize the ValueTypeOf traits class.
I would propose to create adapters.
Your class should be created with the exact level of personalization that is required by the class:
template <template <class> C, template T>
class Example
{
typedef T Type;
typedef C<T> Container;
};
EDIT: attempting to provide more is nice, but doomed to fail, look at the various expansions:
std::vector<T>: std::vector<T, std::allocator<T>>
std::stack<T>: std::stack<T, std::deque<T>>
std::set<T>: std::set<T, std::less<T>, std::allocator<T>>
The second is an adapter, and so does not take an allocator, and the third does not have the same arity. You need therefore to put the onus on the user.
If a user wishes to use it with a type that does not respect the expressed arity, then the simplest way for him is to provide (locally) an adapter:
template <typename T>
using Vector = std::vector<T>; // C++0x
Example<Vector, bool> example;
I am wondering about the use of parameter packs (variadic templates) here... I don't know if declaring C as template <class...> C would do the trick or if the compiler would require a variadic class then.
You have to give the full template signature, including default parameters, if you want to be able to use the template template parameter the usual way.
template <typename T, template <class U, class V = allocator<U> > class C>
class ExampleTemplate {
C<T> items;
public:
....
};
If you want to handle other containers that the one from the STL, you can delegate container construction to a helper.
// Other specialization failed. Instantiate a std::vector.
template <typename T, typename C>
struct make_container_
{
typedef std::vector<T> result;
};
// STL containers
template <typename T, template <class U, class V = allocator<U> > class C>
struct make_container_<T,C>
{
typedef C<T> result;
};
// Other specializations
...
template <typename T, typename C>
class ExampleTemplate {
make_container_<T,C>::result items;
public:
....
};
I think, it is required to reproduce all template parameters, even default. Note, that Standard itself does not use template template parameters for containter adaptors, and prefers to use regular template parameters:
template < class T , class Container = deque <T > > class queue { ... };
template < class T , class Container = vector <T>, class Compare = less < typename Container :: value_type > > class priority_queue { ... };
The following code will allow you to do something like you're asking for. Of course, this won't work with standard containers, since this has to already be part of the template class that's being passed into the template.
/* Allows you to create template classes that allow users to specify only some
* of the default parameters, and some not.
*
* Example:
* template <typename A = use_default, typename B = use_default>
* class foo
* {
* typedef use_default_param<A, int> a_type;
* typedef use_default_param<B, double> b_type;
* ...
* };
*
* foo<use_default, bool> x;
* foo<char, use_default> y;
*/
struct use_default;
template<class param, class default_type>
struct default_param
{
typedef param type;
};
template<class default_type>
struct default_param<use_default, default_type>
{
typedef default_type type;
};
But I don't really think this is what you're looking for. What you're doing with the containers is unlikely to be applicable to arbitrary containers as many of them will have the problem you're having with multiple default parameters with non-obvious types as defaults.
As the question exactly described the problem I had in my code (--I'm using Visual Studio 2015), I figured out an alternative solution which I wanted to share.
The idea is the following: instead of passing a template template parameter to the ExampleTemplate class template, one can also pass a normal typename which contains a type DummyType as dummy parameter, say std::vector<DummyType>.
Then, inside the class, one replace this dummy parameter by something reasonable. For replacement of the typethe following helper classes can be used:
// this is simply the replacement for a normal type:
// it takes a type T, and possibly replaces it with ReplaceByType
template<typename T, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type
{
using type = std::conditional_t<std::is_same<T, ReplaceWhatType>::value, ReplaceByType, T>;
};
// this sets up the recursion, such that replacement also happens
// in contained nested types
// example: in "std::vector<T, allocator<T> >", both T's are replaced
template<template<typename ...> class C, typename ... Args, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type<C<Args ...>, ReplaceWhatType, ReplaceByType>
{
using type = C<typename replace_type<Args, ReplaceWhatType, ReplaceByType>::type ...>;
};
// an alias for convenience
template<typename ... Args>
using replace_type_t = typename replace_type<Args ...>::type;
Note the recursive step in replace_type, which takes care that types nested in other classes are replaced as well -- with this, for example, in std::vector<T, allocator<T> >, both T's are replaced and not only the first one. The same goes for more than one nesting hierarchy.
Next, you can use this in your ExampleTemplate-class,
struct DummyType {};
template <typename C, typename T>
struct ExampleTemplate
{
replace_type_t<C, DummyType, T> items;
};
and call it via
int main()
{
ExampleTemplate<std::vector<DummyType>, float> a;
a.items.push_back(1.0);
//a.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::list<DummyType>, float> b;
b.items.push_back(1.0);
//b.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::map<int, DummyType>, float> c;
c.items[0]=1.0;
//c.items[0]="Hello"; // prints an error message which shows that DummyType is replaced correctly
}
DEMO
Beside the not-that-nice syntac, this has the advantage that
It works with any number of default template parameters -- for instance, consider the case with std::map in the example.
There is no need to explicitly specify any default template parameters whatsoever.
It can be easily extended to more dummy parameters (whereas then it probably should not be called by users ...).
By the way: Instead of the dummy type you can also use the std::placeholder's ... just realized that it might be a bit nicer.