C++ template specialization between map and multimap - c++

Reading other topic here I've already wrote code detecting if class is associative container[1]. Right now in order to use equal_range I need to detect if it's normal map or multimap. Is there any way I can achieve my goal?
[1] Disambiguate template specialization between map-like and vector-like containers

You might probably add your own type trait:
template<typename>
struct is_map : std::false_type {};
template<typename K, typename V>
struct is_map<std::map<K, V>> : std::true_type {};
WANDBOX example

You could also use the suggestions in this post to specialise based on the existence of at() or operator[]

The original answer works perfectly well for checking if a type is a map or not. However, it doesn't work for testing if a type inherits from a map. I made various attempts at a general solution and finally I came up with the following code:
namespace details {
constexpr bool is_map(const void*) {
return false;
}
template<typename K, typename V, typename Comp, typename Alloc>
constexpr bool is_map(const std::map<K, V, Comp, Alloc>*) {
return true;
}
}
template<typename T>
constexpr bool is_map_v = details::is_map(static_cast<const T*>(nullptr));
using map1 = std::map<int, int>;
static_assert(is_map_v<map1>);
struct MyMap : public std::map<int, int>{};
static_assert(is_map_v<MyMap>);
static_assert(is_map_v<int> == false);
It has 2 overloads for the is_map function. One matches pointers to maps, and the other matches everything else. Because they take pointers, passing the address of something publicly derived from map will also be accepted.
The is_map(const void *) will only match if T can't possibly be a map.

Related

function of generic container of specific specialization [duplicate]

This question already has an answer here:
partial type as template argument c++ [duplicate]
(1 answer)
Closed 6 years ago.
I'm trying to write an algorithm that should work with different containers (std::vector, QVector) containing the same type:
template<class Container>
boolean findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
This one gives me
'Container' is not a template
template<template<typename> class Container>
I get:
error: no matching function for call to 'findpeaks(cv::MatExpr, std::vector >&)'
...
note: template argument deduction/substitution failed:
error: wrong number of template arguments (2, should be 1)
Calling code:
cv::Mat m(data, true);
std::vector<std::pair<int, double>> peaks;
QVERIFY(daf::findpeaks(m.t(), peaks));
I've also tried something like this:
template<template< template<typename, typename> typename > class Container>
warning: ISO C++ forbids typename key in template template parameter; use -std=c++1z or -std=gnu++1z [-Wpedantic]
And some more errors...
std::vector has two template parameters.
template<
class T,
class Allocator = std::allocator<T>
> class vector;
And QVector has one. You can do it with variadic template:
template<template <typename...> class Container>
bool findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
Do you actually need Container to be a class template? Just make it a normal type:
template<class Container>
boolean findpeaks(cv::Mat &m, Container& peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
This will let you use other containers that are potentially not templates. Like, struct MySpecialPairContainer { ... };
You may do
template<template <typename ...> class Container>
bool findpeaks(cv::Mat &m, Container<std::pair<int, double>> &peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
Your problem is that std::vector has 2 template parameters, the type T and an allocator.
But you can do even simpler:
template<typename Container>
bool findpeaks(cv::Mat& m, Container& peaks) {
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
Just as expressed with #Barry's answer, I don't think you need a template template parameter here.
However, you seem to have concern about expressing "a container that supports push_back with a pair of..."
I suggest you to express this constraint in a sfinae constraint expression instead. Even if your parameter is explicitly requiring std::pair to be in the first template parameter of a class, it doesn't mean it has a push_back function, and doesn't mean the supposedly existing push_back is taking a std::pair as parameter.
Arguments of a function is currently a bad way of expressing what a template type should be able to do, or should be. You'll have to wait for concepts for that.
In the meantime, you can use a sfinae constraint in your function signature that explicitly express that you need a type that has a member push_back function that accept a std::pair:
template<class Container>
auto findpeaks(cv::Mat &m, Container& peaks)
// Using trailing return type
-> first_t<bool, decltype(peaks.push_back(std::make_pair(1, 1.0)))>
// Here's the constraint -^ that expression need to be valid
{
// do stuff
peaks.push_back(std::make_pair(1, 1.0));
return true;
}
first_t can be implemented that way:
template<typename T, typename...>
using first_t = T;
For the function to exist, the expression inside the decltype must be valid. If the contraint is not satified, the compiler will try other overloads of the function findpeaks.
In general, you shouldn't over specify. If I wrote:
struct my_thing {
void push_back( std::pair<int, double> const& ) {}
};
shouldn't I be able to pass my_thing to your findpeaks?
There is absolutely no need for the template<class...>class Container template within the function, so requring it in the interface is an overspecification.
What you need is a sink (graph theoretical sink -- a sink is where things flow into, and don't flow out of) that consumes pairs of int, double. Ideally you want to be able to pass in a container without extra boilerplate.
template<class T>
struct sink:std::function<void(T)> {
using std::function<T>::function;
// more
};
now your function looks like:
bool findpeaks(cv::Mat &m, sink<std::pair<int, double>const&> peaks) {
// do stuff
peaks(std::make_pair(1, 1.0));
return true;
}
and as a bonus, you can now put it into a cpp file instead of a header. (The dispatching costs for a std::function are modest).
This does require that you wrap the second parameter up at the call site:
std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, [&](auto&& e){v.push_back(decltype(e)(e));} ) {
// ...
which you might not like. Because we didn't use a naked std::function but instead a sink, we can get around this. First we write a metatrait, then some traits.
namespace details {
template<template<class...>class Z, class alwaysvoid, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
This is a meta trait that lets us write other traits.
template<class C, class X>
using push_back_result = decltype( std::declval<C>().push_back( std::declval<X>() ) );
template<class C, class X>
using can_push_back = can_apply< push_back_result, C, X >;
now we have a trait can_push_back that is true_type if and only if you can push an X into the container C.
We now augment sink:
template<class T, class Base=std::function<void(T)>>
struct sink:Base {
using Base::Base;
template<class C,
std::enable_if_t< can_push_back< C&, T >{}, int> =0
>
sink( C& c ):
Base( [&](auto&& t){ c.push_back(decltype(t)(t)); } )
{}
sink()=default;
sink(sink const&)=default;
sink(sink &&)=default;
sink& operator=(sink const&)=default;
sink& operator=(sink &&)=default;
};
This newly augmented sink now can be passed a container that supports .push_back(T) and automatically writes a function that solves the problem for you.
std::vector<std::pair<int, double>> v;
if(findpeaks( matrix, v ) {
// ...
That just works.
We can do the same for containers that support .insert(T), and after that we can use std::set<T> or even std::map<int, double> as a sink for your algorithm:
std::set<std::pair<int, double>> s;
if(findpeaks( matrix, s ) {
// ...
std::map<int, double> m;
if(findpeaks( matrix, m ) {
// ...
Finally, this also supports mocking. You can write a test-sink that helps unit-test your findpeaks algorithm directly.
I find the concept of sink used sufficiently often that having a sink type that supports these kind of things makes my code clearer, and reduces its dependency on any one kind of container.
Performance wise, the cost of the type erasure in std:function is modest. if you really need performance improvement, swapping sink<X> for sink<X, F> where F is a free parameter is possible, and writing make_sink that creates a sink where Base is a lambda, should work with near zero changes in the body of the code. But before you do that, you can work on higher level optimizations, like having the output into sink be processed in a streaming manner, or fed to an async queue, or the like.

meta function with default behavior on missing trait and how to detect random access

The following meta-function computes if a given type is a random access iterator:
template <class I>
struct is_random_access
: boost::is_convertible
< typename boost::iterator_traversal<I>::type
, boost::random_access_traversal_tag
>
{};
Now of course, this doesn't work if I is not an iterator at all, as there is no boost::iterator_traversal<I> is defined.
Two independent questions:
How to make is_random_access return false (rather than fail to compile) when I is not an iterator?
Is this a good way to detect if an iterator is random access traverseable?
For your first question you can actually use the SFINEA example from wikipedia:
template <class... Ts> using void_t = void;
template <class I, class = void>
struct is_random_access: boost::false_type
{};
template <class I>
struct is_random_access<I,void_t<typename std::iterator_traits<I>::iterator_category> >
: boost::is_convertible
<typename boost::iterator_traversal<I>::type, boost::random_access_traversal_tag>
{};
If the input type does not have the iterator_category type defined, it will fall back to the default struct, if it is defined, it will use your specialization.
For the second question I am no expert. However, my interpretation agrees with yours: for every iterator with std::random_access_iterator_tag, the boost::iterator_traversal<>::type will be convertible to boost::random_access_traversal_tag.
Update: Fixed the problem that int* is not regcognized as a valid iterator. Replaced I:iteratory_category by std::iterator_traits<I>::iterator_category.

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

Templates and STL

The following code represents a container based on std::vector
template <typename Item>
struct TList
{
typedef std::vector <Item> Type;
};
template <typename Item>
class List
{
private
typename TList <Item>::Type items;
....
}
int main()
{
List <Object> list;
}
Is it possible to templatize std::vector and create a general container, something like that?
template <typename Item, typename stl_container>
struct TList
{
typedef stl_container<Item>;
};
where stl_container represents std::vector, std::list, std::set...? I would like to choose the type of container at the time of the creation.
List <Object, std::vector> list; //vector of objects, not a real code
List <Object, std::vector> list; //list of objects, not a real code
Thanks for your answers...
Updated question:
I tried the following code but there are errors:
#include <vector>
template <typename Item, typename Container>
struct TList
{
typedef typename Container <Item>::type type; //Error C2059: syntax error : '<', Error C2238: unexpected token(s) preceding ';
};
template <typename T>
struct vector_container
{
typedef std::vector<T> type;
};
int _tmain(int argc, _TCHAR* argv[])
{
TList <int, vector_container> v;
TList <int, map_container> m;
}
Yes, but not directly:
template <typename Item, template <typename> class Container>
struct TList
{
typedef typename Container<Item>::type type;
};
Then you can define different container policies:
template <typename T>
struct vector_container
{
typedef std::vector<T> type;
};
template <typename T>
struct map_container
{
typedef std::map<T, std::string> type;
};
TList<int, vector_container> v;
TList<int, map_container> m;
A bit verbose, though.* To do things directly, you'd need to take the route described by James, but as he notes this is ultimately very inflexible.
However, with C++0x we can do this just fine:
#include <map>
#include <vector>
template <typename Item,
template <typename...> class Container, typename... Args>
struct TList
{
// Args lets the user specify additional explicit template arguments
Container<Item, Args...> storage;
};
int main()
{
TList<int, std::vector> v;
TList<int, std::map, float> m;
}
Perfect. Unfortunately there's no way to reproduce this in C++03, except via the indirection policy classes introduce as described above.
*I want to emphasize that by "A bit verbose" I mean "this is unorthodox". The correct solution for your problem is what the standard library does, as Jerry explains. You just let the user of your container adapter specify the entire container type directly:
template <typename Item, typename Container = std::vector<Item>>
struct TList
{};
But this leaves a big problem: what if I don't want the value type of the container to be Item but something_else<Item>? In other words, how can I change the value type of an existing container to something else? In your case you don't, so read no further, but in the case we do, we want to rebind a container.
Unfortunately for us, the containers don't have this functionality, though allocators do:
template <typename T>
struct allocator
{
template <typename U>
struct rebind
{
typedef allocator<U> type;
};
// ...
};
This allows us to get an allocator<U> given an allocator<T>. How can we do the same for containers without this intrusive utility? In C++0x, it's easy:
template <typename T, typename Container>
struct rebind; // not defined
template <typename T, typename Container, typename... Args>
struct rebind<T, Container<Args...>>
{
// assumes the rest are filled with defaults**
typedef Container<T> type;
};
Given std::vector<int>, we can perform rebind<float, std::vector<int>>::type, for example. Unlike the previous C++0x solution, this one can be emulated in C++03 with macros and iteration..
**Note this mechanism can be made much more powerful, like specifying which arguments to keep, which to rebind, which to rebind themselves before using as arguments, etc., but that's left as an exercise for the reader. :)
I'm a bit puzzled why some very smart (and competent) people are saying no.
Unless I've misread your question, what you're trying to accomplish is virtually identical to the "container adapters" in the standard library. Each provides an interface to some underlying container type, with the container type that will be used provided as a template parameter (with a default value).
For example, a std::stack uses some other container (e.g., std::deque, std::list or std::vector) to hold the objects, and std::stack itself just provides a simplified/restricted interface for when you just want to use stack operations. The underlying container that will be used by the std::stack is provided as a template parameter. Here's how the code looks in the standard:
namespace std {
template <class T, class Container = deque<T> >
class stack {
public:
typedef typename Container::value_type value_type;
typedef typename Container::size_type size_type;
typedef Container container_type;
protected:
Container c;
public:
explicit stack(const Container& = Container());
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
value_type& top() { return c.back(); }
const value_type& top() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
}
Of course, perhaps I've just misunderstood the question -- if so, I apologize in advance to the people with whom I'm (sort of) disagreeing.
Yes and no.
You can use a template template parameter, e.g.,
template <typename Item, template <typename> class Container>
struct TList { /* ... */ };
However, in general, template template parameters are not particularly useful because the number and types of the template parameters have to match. So, the above would not match std::vector because it actually has two template parameters: one for the value type and one for the allocator. A template template parameter can't take advantage of any default template arguments.
To be able to use the std::vector template as an argument, TList would have to be declared as:
template <typename Item, template <typename, typename> class Container>
struct TList { /* ... */ };
However, with this template, you wouldn't be able to use the std::map template as an argument because it has four template parameters: the key and value types, the allocator type, and the comparator type.
Usually it is much easier to avoid template template parameters because of this inflexibility.
well, you can hack it up with a macro:
template <typename T, typename stl_container = std::vector<T> >
struct TList
{
typedef stl_container Type;
};
#define TLIST(T, C) TList<T, C<T> >
TList<int> foo;
TList<int, std::list<int> > bar;
TLIST(int, std::list) baz;
Is it possible to templatize std::vector and create a general container, something like that?
No. You would have to templatize the function or object using the container -- you couldn't templatize the container itself.
For example. consider a typical std::find:
template<class InputIterator, class T>
InputIterator find ( InputIterator first, InputIterator last, const T& value )
{
for ( ;first!=last; first++) if ( *first==value ) break;
return first;
}
This works for any container, but doesn't need a tempalte with the container at all.
Also, given that it looks what you're trying to do is make container independent code, you might want to buy or borrow yourself a copy of Scott Meyers' Effective STL and read Item 2: Beware the illusion of container-independent code.
You can use template template parameters as others have mentioned here. The main difficulty with this is not that dissimilar container types have dissimilar template parameters, but that the standard allows the standard containers, like vector, to have template parameters in addition to the documented, necessary ones.
You can get around this by providing your own subclass types that accept the appropriate template parameters and let any extras (which have to have defaults) be filled in my the implementation:
template < typename T > struct simple_vector : std::vector<T> {};
Or you can use the templated typedef idiom:
template < typename T > struct retrieve_vector { typedef std::vector<T> type; };

Returning a template of templates

This code does not automatically infer the return type correctly (a design aspect of C++):
template < typename Container,
typename UnaryOp>
Container
mymap(Container c, UnaryOp op)
{
typedef typename Container::value_type ResultType
Container<ResultType> result;
for(Container::iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
What I would like to do is have something like this happen:
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic;
foomatic = mymap(bar, [] (string s)->int {return atoi(s.c_str());});
//foomatic now is equal to {1,2,3}
I was figuring that Container would be inferred to be vector, and ResultType would be inferred to be int.
After the question changed:
You are using the same type, Container, for input and output. But your input and output types are distinct: your input is vector<string>, whereas your output is vector<int>. No wonder that C++ refuses to compile this.
Your problem is now to deduce the return type from the input types. Generally, C++ cannot do that. It’s as simple as that: overload resolution and template resolution only happens based on the input arguments, never on the return type (in some cases elaborate tricks involving proxy objects and implicit casts can be used to work around this but let’s not go there).
The simplest and most idiomatic solution is just to specify the return element type manually when calling the function, as in:
foomatic = mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
This requires that the return element type be put first in the template argument list:
template <
typename ResultType,
template<typename> class Container,
typename InputType,
typename UnaryOp>
Container<ResultType> mymap(Container<InputType> c, UnaryOp op) { ... }
However, that does not work because std::vector does not fit the declaration of template<typename> class. Why? Simple reason: because it has more than just one template argument. In particular, the standard says that it has at least one extra template argument to specify the allocator.
Solution: declare the template argument as template<typename, typename> class, right?
No. Now, this does work for some standard library implementations. But besides the mandated two template arguments, the containers may have additional template arguments that take default values (this is often used to pass policy classes to a container, for example; the allocator is already such a policy class).
This is a fundamental problem: we cannot declare Container so that it conforms to all possible type signatures of containers in C++. So this solution, too, is a no-go.
The best solution is unfortunately more complicated, we need to rebind the container type explicitly. This we can do via an extra metafunction:
template <typename C, typename T>
struct rebind;
We need to partially specialize this metafunction for each possible number of template parameters. For example, to make it work with the minimal std::vector, we’d need the following partial specialization:
template <
template <typename, typename> class C,
typename Old,
typename New,
typename A>
struct rebind<C<Old, A>, New> {
typedef typename A::template rebind<New> Rebound;
typedef C<New, typename Rebound::other> type;
};
This looks daunting. What it does is take an existing std::vector<foo> and a type bar and rewrite it to a std::vector<bar>. The tricky part is that we also need to rewrite the allocator type. This is done via the rather complicated Rebound declaration.
Now we can write your function, and invoke it:
template <
typename ResultType,
typename C,
typename UnaryOp>
typename rebind<C, ResultType>::type
mymap(C const& c, UnaryOp op)
{
typename rebind<C, ResultType>::type result;
for(typename C::const_iterator i = c.begin();
i != c.end();
i++)
{
result.push_back(op(*i));
}
return result;
}
int main() {
vector<string> bar;
bar.push_back("1");
bar.push_back("2");
bar.push_back("3");
vector<int> foomatic =
mymap<int>(bar, [] (string s)->int {return atoi(s.c_str());});
}
Piece of cake. A really, really, complicated cake.
Answer to old question:
If you have a template parameter that is itself a class template, you need to declare it as such:
template <
template<typename> class Container,
typename ResultType,
typename UnaryOp>
Container<ResultType> mymap(Container<ResultType> c, UnaryOp op) { ... }
The template<typename> class Container mimics the class template declaration syntax and tells the compiler that “Container is a class template that expects a single template argument.”
But libraries usually avoid these nested template declarations and instead rely on traits/metafunctions to communicate such information. That is, it would usually be written as follows:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Container::value_type ResultType;
}
(The typename in the typedef is necessary because the name is a dependent name and C++ cannot figure out that this it names a type.)
This example mimics the standard library convention of having a typedef value_type inside each container for its associated value type. Other libraries may follow different schemas. For example, I am contributing to a library that uses external metafunctions that work as follows:
template <typename Container, typename UnaryOp>
Container mymap(Container c, UnaryOp op) {
typedef typename Value<Container>::Type ResultType;
}
The idea is the same, the only difference is that Container::value_type has been “outsourced” to an independent type.
You need something along the lines of:
template<typename Container, typename UnaryOp>
auto mymap(Container c, UnaryOp op) -> Container::rebind<decltype(op(*c.begin()))>
{
typedef typename Container::value_type InputType;
typedef decltype( op( InputType() ) ) ResultType;
typedef typename Container::rebind<ResultType> ResultContainer;
// ...
}
You can use a trick known as auto_cast, which we will rewrite a little bit to be specific to containers.
template<typename container> struct auto_cast_container {
container c;
template<typename out_type> operator out_type() {
return out_type(c.begin(), c.end());
}
};
template<typename Container, typename UnaryOperator>
auto
mymap(const Container& c, UnaryOperator op)
-> auto_cast_container<std::vector<decltype(op(*c.begin()))>> {
std::vector<decltype(op(*c.begin()))> retval;
std::for_each(c.begin(), c.end(), [&](decltype(*c.begin())& ref) {
retval.push_back(op(ref));
});
auto_cast_container<std::vector<decltype(op(*c.begin()))>> return_value;
return_value.c = std::move(retval);
return return_value;
}
Effectively, the templated conversion operator allows for conversion to any type which will accept the begin/end constructor. This means that you can map from a vector to a list, if you like, and it can also map pairs into associative containers and back again, should you need it. If you're hunting for efficiency this can be tuned further but I left that out for clarity.
Edit: Konrad's comment pointed out a couple of logical flaws. I also improved the safety and transparency of the system by using decltype in all appropriate cases.