I'm trying to provide a generic mapping function over STL containers such as vector and list. Here's my implementation:
#include <functional>
#include <algorithm>
template<class A, class B, template <class> class F>
F<B> fmap(F<A> &functor, std::function<B(A)> &f)
{
F<B> newFunctor;
return std::transform(begin(functor)
, end(functor)
, begin(newFunctor)
, f);
}
But when I try and call it with the code:
vector<int> v;
for(int i = 0; i < 5; i++) {
v.push_back(i);
}
vector<int> w = fmap(v, [](int i) { return i + 1; });
I get a no matching function call error.
How can I get this to work?
There are a couple of things wrong in the code. The first thing, as already pointed out, is that the std::vector template takes 2 template arguments, the stored type and the allocator. Even though the second is defaulted to be the instantiation of std::allocator with the stored type, it is still an argument to the template.
The second problem you will encounter is that although you can create a std::function from a lambda, the lambda expression is not a std::function, so the compiler will fail to match the second argument.
vector had 2 template arguments
template < class T, class Alloc = allocator<T> > class vector;
You can obtain some independence from the exact declaration structure of the container type by assuming that all applicable container types are template classes whose first parameter is the value type. For output, produce a container with a different value type and all other template parameters the same. This is possible with a type trait that can rebind containers with differing value type:
template <typename Container>
struct container_rebind;
template <template <typename...> class Container, typename ValueType, typename... OtherArgs>
struct container_rebind<Container<ValueType, OtherArgs...>> {
template <typename NewValueType>
using rebind = Container<NewValueType, OtherArgs...>;
};
template <typename Container, typename NewValueType>
using ContainerRebind = typename container_rebind<Container>::template rebind<NewValueType>;
so, e.g., ContainerRebind<std::vector<int>, double> is std::vector<double>. It's possible to partially specialize container_rebind to support other kinds of container templates, such as those that have non-type template parameters. Adding support for std::array, for example, is left to the reader.
Related
This is the function I have, it sorts a vector of pairs by the first element of the pair.
std::vector<std::pair<int,std::string>>
sort_pairs(std::vector<std::pair<int,std::string>>&& items)
{
std::sort(std::begin(items),std::end(items));
return items;
}
But when I try to generalize it to accept any type of allocator and any type for the pair i get a bunch of errors (too many to paste them here). I'm using rvalue reference as argument because the function is called inside other functions, with copy argument also works but I think it would be less efficient. Here is some approach I've tryed.
template <typename Allocator,
typename P1 = typename Allocator::value_type::first_type,
typename P2 = typename Allocator::value_type::second_type>
Allocator<std::pair<P1,P2>> sort_pairs(Allocator<std::pair<P1,P2>>&& items)
{
std::sort(std::begin(items),std::end(items));
return items;
}
I'd appreciate if you add some c++20 hints to the code. Like requirements.
Is the fact that the underlying type is a pair salient? The body of the algorithm doesn't make use of that in any way. We can start with just:
template <typename R>
std::decay_t<R> sort(R&& items)
{
std::sort(std::begin(items),std::end(items));
return items;
}
Which in C++20 we can properly constrain by requiring that R is a range whose iterators are sortable - yeah, there's a concept for that:
template <std::ranges::range R>
requires std::sortable<std::ranges::iterator_t<R>>
std::decay_t<R> sort(R&& items)
{
std::sort(std::ranges::begin(items), std::ranges::end(items));
return items;
}
If you really want to require that this is a range of pairs, you can add that as a separate constraint:
template <std::ranges::range R>
requires std::sortable<std::ranges::iterator_t<R>> &&
is_specialization_of<std::ranges::range_value_t<R>, std::pair>
std::decay_t<R> sort(R&& items)
{
std::sort(std::ranges::begin(items), std::ranges::end(items));
return items;
}
I'll leave the implementation of is_specialization_of as an exercise.
What you should use, to express your Allocator, is a "template template parameter".
As follows
template <template <typename...> class Allocator,
typename P1, typename P2>
Allocator<std::pair<P1, P2>>
sort_pairs (Allocator<std::pair<P1, P2>> && items)
{
std::sort(std::begin(items),std::end(items));
return items;
}
Starting from C++17 you can use typename instead of class defining it
// ..............................VVVVVVVV
template <template <typename...> typename Allocator,
but class is still valid and, IMHO, preferable.
Observe that Allocator intercept std::vector (the template class, not a specific specialization as std::vector<std::pair<int, std::string>>) so you can't extract P1 and P2 from it as in your example
// ....................VVVVVVVVV not usable this way
typename P1 = typename Allocator::value_type::first_type
you should write
typename Allocator<std::pair<P1, P2>>::value_type::first_type
but, obviously, you can't use it to give a default type to P1 because you have to know P1.
Fortunately, P1 and P2 can be deduced from the items argument, so you don't need default types.
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.
Say I have a class Foo which uses two different generic types, one is _Type and the other is _Comparator. _Type is known to be a std::vector, std::list, or std::string, so it will have a type within it: T will be within vector and list; char will be within string.
My other generic type _Comparator is an optional template parameter, by which the user may specify her own less than function, functor, or lambda function. If no argument is provided as the second template parameter, it should default to the std::less<M> functor, whereby type M shall be the type of elements contained within _Type.
I do not know the syntax for how to do this.
I've tried:
template <typename _Type<T>, typename _Comparator = less<T> >
to no avail.
Using the approach mentioned by #Joachim Pileborg in the comments, I was able to come up with the following, which allowed me to access the inner type of _Type:
template <typename _Type,
typename _Comparator = less<typename _Type:: value_type> >
class Foo
{
public:
// some methods
private:
_Type sequence;
_Comparator comparator;
};
and now std::less compares the correct types without complaining.
if I understood your question correctly, you might try something like:
#include <iostream>
#include <vector>
#include <list>
#include <typeinfo>
template <typename T>
class holder
{
public:
template <typename Type = std::vector<T>, typename Comparator = std::less<T> >
class impl
{
public:
impl() {std::cout << typeid(s).name() << std::endl; }
Type s;
};
} ;
int main()
{
holder<int>::impl<> holder_of_int_vectors;
holder<int>::impl<std::list<int> > holder_of_int_lists;
holder<int>::impl<std::list<int>, std::greater<int> > holder_of_int_lists_with_greater;
}
ie, use an external class to hold the "basic" type (T) and an internal for the container (Type) and comparator.
As you said you only want to support vector, list, and string, you can use this:
template <typename T, typename Compare = std::less<typename T::value_type>>
This will support all types which have a member typedef value_type, which vector, list, and string all do.
It's possible to support other types using variadic template template parameters, but that's getting much more complicated.
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'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.