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

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.

Related

Divorce a parameter pack in a class template

I am trying to write a class template that uses a parameter-pack and implements a member function for each type contained in the parameter-pack.
This is what I have so far:
template <typename...T>
class Myclass {
public:
void doSomething((Some_Operator_to_divorce?) T) {
/*
* Do Something
*/
std::cout << "I did something" << std::endl;
}
};
My goal is to have a class template that can be used in the following way:
Myclass<std::string, int, double> M;
M.doSomething("I am a String");
M.doSomething(1234);
M.doSomething(0.1234);
Where the class template mechanism will create an implementation for a doSomething(std::string x), a doSomething(int x) and a doSomething(double x) member function but not a doSomething(std::string x, int i, double f) member function.
I found a lot of examples in the web on the usability of parameter-packs, but I could not figure out if it can be used for my purpose, or if I totally misunderstood for what a parameter-pack can be used.
I thought that I need to unpack the parameter-pack but, after reading a lot of examples about unpacking parameter packs, I believe that this is not the right choice and it has a complete different meaning.
So, therefore, I am looking for a operation to "divorce" a parameter-pack.
There is no "operator" specifically that supports this, but what you're requesting can be done in a few different ways, depending on your requirements.
The only way to "extract" T types from a parameter pack of a class template with the purpose of implementing an overload-set of functions is to implement it using recursive inheritance, where each instance extracts one "T" type and implements the function, passing the rest on to the next implementation.
Something like:
// Extract first 'T', pass on 'Rest' to next type
template <typename T, typename...Rest>
class MyClassImpl : public MyClassImpl<Rest...>
{
public:
void doSomething(const T&) { ... }
using MyClassImpl<Rest...>::doSomething;
};
template <typename T>
class MyClassImpl<T> // end-case, no more 'Rest'
{
public:
void doSomething(const T&) { ... }
};
template <typename...Types>
class MyClass : public MyClassImpl<Types...>
{
public:
using MyClassImpl<Types...>::doSomething;
...
};
This will instantiate sizeof...(Types) class templates, where each one defines an overload for each T type.
This ensures that you get overload semantics -- such that passing an int can call a long overload, or will be ambiguous if there are two competing conversions.
However, if this is not necessary, then it'd be easier to enable the function with SFINAE using enable_if and a condition.
For exact comparisons, you could create an is_one_of trait that only ensures this exists if T is exactly one of the types. In C++17, this could be done with std::disjunction and std::is_same:
#include <type_traits>
// A trait to check that T is one of 'Types...'
template <typename T, typename...Types>
struct is_one_of : std::disjunction<std::is_same<T,Types>...>{};
Alternatively, you may want this to only work if it may work with convertible types -- which you might do something like:
template <typename T, typename...Types>
struct is_convertible_to_one_of : std::disjunction<std::is_convertible<T,Types>...>{};
The difference between the two is that if you passed a string literal to a MyClass<std::string>, it will work with the second option since it's convertible, but not the first option since it's exact. The deduced T type from the template will also be different, with the former being exactly one of Types..., and the latter being convertible (again, T may be const char*, but Types... may only contain std::string)
To work this together into your MyClass template, you just need to enable the condition with SFINAE using enable_if:
template <typename...Types>
class MyClass
{
public:
// only instantiates if 'T' is exactly one of 'Types...'
template <typename T, typename = std::enable_if_t<is_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
// or
// only instantiate if T is convertible to one of 'Types...'
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T&) { ... }
};
Which solution works for you depends entirely on your requirements (overload semantics, exact calling convension, or conversion calling convension)
Edit: if you really wanted to get complex, you can also merge the two approaches... Make a type trait to determine what type would be called from an overload, and use this to construct a function template of a specific underlying type.
This is similar to how variant needs to be implemented, since it has a U constructor that considers all types as an overload set:
// create an overload set of all functions, and return a unique index for
// each return type
template <std::size_t I, typename...Types>
struct overload_set_impl;
template <std::size_t I, typename T0, typename...Types>
struct overload_set_impl<I,T0,Types...>
: overload_set_impl<I+1,Types...>
{
using overload_set_impl<I+1,Types...>::operator();
std::integral_constant<std::size_t,I> operator()(T0);
};
template <typename...Types>
struct overload_set : overload_set_impl<0,Types...> {};
// get the index that would be returned from invoking all overloads with a T
template <typename T, typename...Types>
struct index_of_overload : decltype(std::declval<overload_set<Types...>>()(std::declval<T>())){};
// Get the element from the above test
template <typename T, typename...Types>
struct constructible_overload
: std::tuple_element<index_of_overload<T, Types...>::value, std::tuple<Types...>>{};
template <typename T, typename...Types>
using constructible_overload_t
= typename constructible_overload<T, Types...>::type;
And then use this with the second approach of having a function template:
template <typename...Types>
class MyClass {
public:
// still accept any type that is convertible
template <typename T, typename = std::enable_if_t<is_convertible_to_one_of<T, Types...>::value>>
void doSomething(const T& v)
{
// converts to the specific overloaded type, and call it
using type = constructible_overload_t<T, Types...>;
doSomethingImpl<type>(v);
}
private:
template <typename T>
void doSomethingImpl(const T&) { ... }
This last approach does it two-phase; it uses the first SFINAE condition to ensure it can be converted, and then determines the appropriate type to treat it as and delegates it to the real (private) implementation.
This is much more complex, but can achieve the overload-like semantics without actually requiring recursive implementation in the type creating it.

Can I use std::vector as a template parameter or does it need to be std::vector<T>?

I know this is a simple question but I just could not find the answer.
I am trying to do something like this but instead of with std::vector ultimately I want it to be std::shared_ptr or std::weak_ptr:
template <int dim, class ChunkClass, class PtrClass>
class BaseChunkWindow : public IChunkWindow<BaseChunkWindow<dim, ChunkClass, PtrClass>, IChunk<ChunkClass>> {
public:
...
private:
PtrClass< IChunk<ChunkClass> > ptr; <-- compiler doesn't like this line, however IChunk<ChunkClass>* works
};
It depends on what you are passing it to, if the template you're trying to instantiate takes as a parameter a class template accepting 2 (or in c++11 a variadic number of) types then you can pass std::vector to that. In most cases however, templates require types as parameters and you cannot pass the class template std::vector.
template <class T>
struct gimme_a_type{};
template <template <class,class> class T>
struct gimme_a_template{};
gimme_a_type<std::vector> //<-- does not compile, expected a type, got a template
gimme_a_type<std::vector<int> > //<-- compiles, expected a type, got a type
gimme_a_template<std::vector> //<-- compiles, expected a template, got a template that has the correct signature
gimme_a_template<std::vector<int> > //<-- does not compile, expected a template, got a type
In response to your edit, there are difficulties to using class templates as template parameters. Matching the number of parameters exactly is actually difficult to do when you have default arguments in the class template you're trying to pass (std::vector in our case).
Notice that the example above required a class template that takes 2 types, not just one. This is because std::vector takes two parameters, the second is just defaulted to std::allocator<T> for us.
The following example demonstrates the issue:
template <template <class, class> class Tem>
struct A
{
Tem<int> v; //<-- fails to compile on gcc, Tem takes two parameters
Tem<int, std::allocator<int> >; //<-- compiles, but requires a priori knowledge of Tem
};
template <template <class...> class Tem>
struct A2
{
Tem<int> v; //<-- This C++11 example will work, but still isn't perfect.
};
The C++11 example is better, but if someone passed a class that has as a signature template <class, bool = false> class A3 it fails again because A2 requires a class template that takes types and not a mix of types and non-types (false being the non-type template parameter in this example). So even though A3<int> would be a valid instantiation you couldn't pass that class to A2.
The solution there is to always use types in template parameter lists and use the std::integral_constant wrapper template to pass integral constants around.
There are a couple ways of doing it.
The limited way would be to use a template template parameter with just a limited number of parameters being passed, e.g. 3.
template<template<class,class,class> class Cont, class T, class V, class U>
void f(Cont<T,V,U>&& cont) {
//...
}
However that's pretty limiting and can be hard to manage if you decide to change it in the future.
So you can do it like so with the new Variadic Templates in C++11:
template<template<class...> class Cont, typename F, typename... Rest>
void f(Cont<F, Rest...>&& cont) {
//...
}
This would work on other containers or things and is probably much easier to manage.

C++11 "late binding" of template arguments

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.

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