I have a template function that takes a container as a parameter
(I use this function for both vector, set, map so trying to avoid it would cost a lot of code copying)
so naturally I declared it as:
template<template<class T, class Allocator = std::allocator<T>> class
Container> Container<std::weak_ptr<A>>* Foo() {...}
Note that the return value of Foo uses the default parameter of the container.
When I use the function with vector, list or set it works just fine;
the problem I have is when I try to use this template with a map container with the comparator and value type fixed (which is defined as MapToValue<ValueType>::FromKey with A and Comparator<Key> already defined):
template<class Value> class MapToValue {
template<class Key, class Allocator = std::allocator<std::pair<const
Key, Value>>> FromKey : public std::map<Key, Value, Comparator<Key>,
Allocator> {...}
In this case the default value of the allocator is diffrent then the one in the definition of Foo - std::allocator<std::pair<_Ty, std::string>> vs. std::allocator<_Ty>.
Long story short, I need that to send to Foo a container with a second parameter that can be defaulted WITHOUT knowing what this default type would be (so this function template can be used for both map, vector or basically any other container). Is this possible?
EDIT: I cannot use C++11 in any way, the compiler is gcc 4.1.2 (out of my control)
In c++11 you can take any template as template argument:
template<template <class ...> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
There's a misunderstanding here of how template template parameters operate. This declaration:
template<template<class T, class Allocator = std::allocator<T>> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
Is just an overly verbose version of this declaration:
template < template <class, class> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
(What is A btw?)
It doesn't matter what the names or defaults of the types are that Container takes - Foo is simply templated on some class template that takes two template type arguments. That works with vector because it is a class template that takes two template type arguments:
template<
class T,
class Allocator = std::allocator<T>
> class vector;
It does not work with map because that one takes four template type arguments:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
In C++11, you can generalize your function to have a template template argument take an arbitrary number of template type arguments:
template < template <class...> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
Though, however, this is never going to work as some container types can't be constructed with only a single template argument (e.g. map).
I found a solution, it isn't elegant but it works:
Sending to Foo another template argument that would be the way to deafault the Allocator:
template<template<typename> class DefaultAllocator, template<typename T,
typename Allocator = DefaultAllocator<T>> class Container>
Container<std::weak_ptr<A>>* Foo() {...}
This way I send Foo<std::allocator,std::vector> all the same.
Also by creating some wrapper template<class T> class MapAllocatorWrapper : std::allocator<std::pair<T,std::string>>, I can send Foo<MapAllocatorWrapper, MapToType<std::string>::FromKey after slight adjustments.
Related
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.
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.
I was reading Modern C++ design and not able to fully understand how template template parameter works.
For example as given in this article http://www.informit.com/articles/article.aspx?p=376878 we can create a Stack with template parameters of type and containers .
If we use just type and container as parameter to template Stack then it may create some issues like
template <typename T, class Cont>
class Stack;
template <typename> class List;
Stack<int, List<int> > aStack1; // OK
Stack<double, List<int> > aStack2; // legal, not OK
Stack<std::string, Deque<char *> > aStack3; // error!
In above code I can understand that aStack1 is fine,aStack2 and aStack3 are issues because if incompatible types between Stack element type and container element type.
As per article this can be resolved if we use template template parameter
template <typename T, template <typename> class Cont>
class Stack;
Stack<int,List> aStack1;
Stack<std::string,Deque> aStack2;
My doubt here is how can Deque knows that its element type is std::string or List element type is int???Is this done by template argument deduction?
Here we are creating a Deque with type T .
If we have defined stack as
template <typename T,template U, template <typename> class Cont>
class Stack;
then how can we instantiate Stack
Stack<std::string,int,Deque> // will this work????
My doubt here is how can Deque knows that its element type is std::string or List element type is int???Is this done by template argument deduction?
No, you explicitly instantiate the class template in the implementation of Stack which is likely to be implemented as:
template <typename T, template <typename> class Cont>
class Stack
{
Cont<T> _container; //here you use `T` as template argument to `Cont`
Cont<int> _ints; //you can do even this if you need a containter of int.
//though in this case you don't need this.
public:
void push(T const & data)
{
_container.push_back(data);
}
//etc
};
For a template to be instantinated its definition is required. In your examples, you only provide the template declaration, which is not enough for it to be instantinated. The definition gives the implementation which details how the template parameters are used. For example,
template <typename T, template <typename> class Cont>
class Stack : Cont<T>
{
typedef Cont<T> my_container;
public:
using my_container::push_back;
/* more details here */
};
Stack<int,List> aStack1;
Stack<std::string,Deque> aStack2;
implements aStack1 as derived from a List<int> and astack2 as derived from a Deque<std::string>.
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
I have a class template, expecting some other template as a parameter:
template<
class Key,
template <typename K,template <typename T> class Allocator> class Policy
>
class container {
Policy<Key,Allocator>* _policy;
//some code here
};
and usually i use it with the policy class like this:
template <class Key,template <typename T> class Allocator> class policy {
//some code
};
but what if i have to pass additional template parameter to policy class? Something like:
template <time_t Age,class Key,template <typename T> class Allocator> class policy_3 {
//some code
};
What can i do, to allow users of that class, pass the Age paratemeter without touching others? For example:
typedef container<key_type,policy_3<100500> > containerWithAge;
You have two options: binding, and rebinding.
In binding, you adapt the ternary policy into a binary one, as expected by the template-template parameter Policy:
template <typename Key, template <typename T> class Allocator>
struct policy_3_100500 : ternary_policy<100500,Key,Allocator> {};
and use policy_3_100500 instead of policy_3<100500>.
To be closer to the syntax you're shooting for, you can use a nested class:
template <time_t Age>
struct policy_3 {
template <typename Key, template <typename T> class Allocator>
struct type : ternary_policy<Age,Key,Allocator> {};
};
and use policy_3<100500>::type instead of policy_3<100500>.
The only way to get exactly the syntax you want is to move the ::type into the class using the policy. That's the second option: rebinding (this is also used in std::allocator, btw). In this case, you pass the Policy as a normal template parameter, and assume a template metafunction, say bind, to exist:
template <time_t Age>
struct policy_3 {
template <typename Key, template <typename T> class Allocator>
struct bind : ternary_policy<Age,Key,Allocator> {};
};
While structually identical to the second option, the difference lies in who calls bind: In the first option (binding), it's the user of the policy class (by passing policy<100500>::type explicitly). Here, it's the class using the policy:
template <typename Key, typename Policy>
struct container {
typename Policy::template bind<Key,std::allocator<Key>> * _policy;
// ...
}:
As a general note, Policy classes are not usually passed as template-template arguments, but as normal template arguments (precisely because they may have a varying number of arguments themselves). The classes using the policy then assume a certain inner structure (typedefs, functions, meta functions, constants) to be present in the policy, of which bind is just an example.