I'd like to have a templated function taking in a vector<T> v and a function op, mapping T to vector<U> and would like to concatenate the results of applying f to every element vector of v to return a vector<U> = [ Elements of op(v[0]), Elements of op(v[1]) ...].
A working option I found was adding an example in the function to allow for template deduction:
template <typename Container>
Container& concat(Container& c1, Container const& c2) {
c1.insert(end(c1), begin(c2), end(c2));
return c1;
}
template <typename Container, typename UnaryOperation, typename U>
inline auto to_vec_from_vectors(Container& c, UnaryOperation&& op, U& ex)
-> std::vector<U> {
std::vector<U> v;
for (auto& e : c) {
std::vector<U> opv = op(e);
concat(v, opv);
}
return v;
}
But naturally I'd like to produce the same result with only the two parameters.
My attempt [replacing U with decltype(*std::begin(op(*std::begin(c))))]:
template <typename Container, typename UnaryOperation, typename U>
inline auto to_vec_from_vectors(Container& c, UnaryOperation&& op, U& ex)
-> std::vector<decltype(*std::begin(op(*std::begin(c))))> {
std::vector<decltype(*std::begin(op(*std::begin(c))))> v;
for (auto& e : c) {
std::vector<decltype(*std::begin(op(*std::begin(c))))> opv = op(e);
concat(v, opv);
}
return v;
}
Unfortunately this didn't compile. I'm also worried of wasting time if op is complex method.
This gave:
error: conversion from ‘std::vector<U>’ to non-scalar type ‘std::vector<const U&, std::allocator<const U&> >’ requested
error: forming pointer to reference type ‘const U&
...
so it seems to be related to 'const'.
How would this variant be corrected? Are there better alternatives?
Dereferencing a container iterator yields a reference (or a const reference, if the container was const), which is why decltype(*std::begin(op(*std::begin(c)))) yields const U& according to your compiler error (and not U).
You can fix this by either removing the reference again with std::remove_reference (or, if you want to also remove const and volatile, std::remove_cvref), or by just asking the vector for what it actually stores:
decltype(*std::begin(op(*std::begin(c)))) -> typename decltype(op(*std::begin(c)))::value_type
I have gone ahead and removed the unneeded U& ex parameter.
template <typename Container, typename UnaryOperation>
inline auto to_vec_from_vectors(Container& c, UnaryOperation&& op)
-> std::vector<typename decltype(op(*std::begin(c)))::value_type> {
std::vector<typename decltype(op(*std::begin(c)))::value_type> v;
for (auto& e : c) {
std::vector<typename decltype(op(*std::begin(c)))::value_type> opv = op(e);
concat(v, opv);
}
return v;
}
Demo
You can also avoid the triple repetition of the decltype chant by naming it:
template <typename Container, typename UnaryOperation>
using applied_op_t = typename decltype(std::declval<UnaryOperation>()(*std::begin(std::declval<Container>())))::value_type;
Related
I have the following map implementation that takes a vector and a lambda as input and returns a new vector.
template <typename R, typename T, typename Predicate>
std::vector<R> map(const std::vector<T> &v, Predicate p) {
std::vector<R> mapd(v.size());
std::transform(v.begin(), v.end(), mapd.begin(), p);
return mapd;
}
Which I use in the following way
std::vector<int> input{1, 2, 3};
auto result = map<float>(input, [] (const int &v) { return v * 2.0; }); // result should be vector<float>{2., 4., 6.}
This works, but I want to see if I can deduce the R template type without specifying it in the call.
Now, I know that I have both the return type of the lambda and the type of the input vector, is there a way to do this? I've tried using std::result_of_t<p(t)> but got nowhere.
Do you mean something as follows ?
template <typename T, typename Predicate,
typename R = decltype(std::declval<Predicate>()(std::declval<T>()))>
std::vector<R> map(const std::vector<T> &v, Predicate p) {
std::vector<R> mapd(v.size());
std::transform(v.begin(), v.end(), mapd.begin(), p);
return mapd;
}
Or maybe (if you can use at least C++14)
template <typename T, typename Predicate>
auto map(const std::vector<T> &v, Predicate p) {
std::vector<decltype(p(v[0]))> mapd(v.size());
std::transform(v.begin(), v.end(), mapd.begin(), p);
return mapd;
}
I try to make map, to convert all std::string element in vector to const char *, here is my code:
template<typename T, typename T2, typename Func>
auto map(std::vector<T> v, Func f) {
std::vector<T2> v2;
for (T i: v) {
v2.push_back(f(i));
}
return v2;
}
I wanna use it with map<const char*>(my_vector, [](std::string v){ v.c_str()}), how to implement the <const char*>
In general, you do it by having the parameter that you want to explicitly specify be the first template parameter:
template<typename T2, typename T, typename Func>
auto map(std::vector<T> v, Func f) {
std::vector<T2> v2;
for (T i: v) {
v2.push_back(f(i));
}
return v2;
}
but you have to be careful in this situation, because you are passing copies of std::string. You'll end up with a vector<const char *> which contains pointers to strings that no longer exist.
Instead, you'll need to pass a reference:
template<typename T2, typename T, typename Func>
auto map(std::vector<T> &v, Func f) { // pass by reference here
std::vector<T2> v2;
for (T& i: v) { // take by reference here
v2.push_back(f(i));
}
return v2;
}
and have your lambda take a reference as well:
[](std::string &v){ return v.c_str(); }
I am wondering if it is possible to write a C++ equivalent of the Python function map, using the automatic return type deduction feature. What I have in mind is something like this:
vector<int> input({1,2,3});
auto output=apply(input,[](int num){return num*num;});
//output should be a vector {1,4,9}
I do know about std::transform, but in the present state of affairs, writing a range loop seems easier.
Baum mit Augen's answer is most of the way there. Just takes a few more steps to support anything that is for-each-able:
template <typename C, typename F>
auto apply(C&& container, F&& func)
{
using std::begin;
using std::end;
using E = std::decay_t<decltype(std::forward<F>(func)(
*begin(std::forward<C>(container))))>;
std::vector<E> result;
auto first = begin(std::forward<C>(container));
auto last = end(std::forward<C>(container));
result.reserve(std::distance(first, last));
for (; first != last; ++first) {
result.push_back(std::forward<F>(func)(*first));
}
return result;
}
We can even go one step further and make this SFINAE-able by not using C++14 auto deduction and instead moving the failure up to the deduction phase. Start with a helper for begin/end:
namespace adl_helper {
using std::begin;
using std::end;
template <typename C>
auto adl_begin(C&& c) -> decltype(begin(std::forward<C>(c))) {
return begin(std::forward<C>(c));
}
template <typename C>
auto adl_end(C&& c) -> decltype(end(std::forward<C>(c))) {
return end(std::forward<C>(c));
}
}
using adl_helper::adl_begin;
using adl_helper::adl_end;
And then use that to deduce E earlier:
using adl_helper::adl_begin;
using adl_helper::adl_end;
template <typename C,
typename F,
typename E = std::decay_t<decltype(std::declval<F>()(
*adl_begin(std::declval<C>())
))>
>
std::vector<E> apply(C&& container, F&& func)
{
/* mostly same as before, except using adl_begin/end instead
of unqualified begin/end with using
*/
}
Now we can test at compile time if some container/function pair is apply-able, and the error is a deduction failure instead of a usage failre:
int arr[] = {1, 2, 3};
auto x = apply(arr, []{ return 'A'; });
main.cpp: In function 'int main()':
main.cpp:45:52: error: no matching function for call to 'apply(int [3], main()::<lambda()>)'
auto x = apply(arr, []() -> char { return 'A'; });
^
main.cpp:29:16: note: candidate: template<class C, class F, class E> std::vector<E> apply(C&&, F&&)
std::vector<E> apply(C&& container, F&& func)
^
main.cpp:29:16: note: template argument deduction/substitution failed:
main.cpp:25:50: error: no match for call to '(main()::<lambda()>) (int&)'
typename E = decltype(std::declval<F>()(
^
As pointed out, this would not handle a container of input iterators well. So let's fix it. We need something to determine the size of the container. If the container has a size() member function, we can use that. Otherwise if the iterators do not have category input_iterator_tag (don't know of any other way to distinguish input iterators...), we can use that. Otherwise, we're kind of out of luck. A good way of doing decreasing order of preference like this is to introduce a chooser hierarchy:
namespace details {
template <int I> struct chooser : chooser<I-1> { };
template <> struct chooser<0> { };
}
And then just walk down:
namespace details {
template <typename C>
auto size(C& container, chooser<2>) -> decltype(container.size(), void())
{
return container.size();
}
template <typename C,
typename It = decltype(adl_begin(std::declval<C&>()))
>
auto size(C& container, chooser<1>)
-> std::enable_if_t<
!std::is_same<std::input_iterator_tag,
typename std::iterator_traits<It>::iterator_category
>::value,
size_t>
{
return std::distance(adl_begin(container), adl_end(container));
}
template <typename C>
size_t size(C& container, chooser<0>)
{
return 1; // well, we have no idea
}
}
template <typename C>
size_t size(C& container)
{
return size(container, details::chooser<10>{});
}
Then we can use size() to reserve() our vector to the best of our ability:
template <typename C,
typename F,
typename E = std::decay_t<decltype(std::declval<F>()(
*adl_begin(std::declval<C>())
))>
>
std::vector<E> apply(C&& container, F&& func)
{
std::vector<E> result;
result.reserve(size(container));
for (auto&& elem : container) {
result.push_back(std::forward<F>(func)(std::forward<decltype(elem)>(elem)));
}
return result;
}
This can certainly be done and would probably look something like this:
template <class Container, class Function>
auto apply (const Container &cont, Function fun) {
std::vector< typename
std::result_of<Function(const typename Container::value_type&)>::type> ret;
ret.reserve(cont.size());
for (const auto &v : cont) {
ret.push_back(fun(v));
}
return ret;
}
If you want to be super general and handle C arrays and everything, you might need to add a couple of overloads for the special cases.
Live example
This has already been discussed in comments, but I think it should also be given as an answer:
The function std::transform from <algorithm> does what you want.
The following code works for me:
#include <vector>
#include <algorithm>
using namespace std;
//...
vector<int> input({1,2,3});
transform(input.begin(), input.end(), input.begin(), [](int num){return num*num;});
This works with your example, and with most of the containers. I use std::transform, because it can be optimized for each stl iterator. I started out from Baum mit Augen's answer, which was deleted later.
template<typename Container, typename Function>
using _mapT = std::vector<typename std::result_of<Function(const typename Container::value_type&)>::type>;
template <typename Container, typename Function>
_mapT<Container, Function> map(const Container &container, Function &&f)
{
_mapT<Container, Function> ret; ret.reserve(container.size());
std::transform(container.begin(), container.end(), std::back_inserter(ret), std::forward<Function>(f));
return ret;
}
I have the following template.
template<typename T, typename U>
std::vector<U> map(const std::vector<T> &v, std::function<U(const T&)> f) {
std::vector<U> res;
res.reserve(v.size());
std::transform(std::begin(v), std::end(v), std::end(res), f);
return res;
}
When I use it in my code I have the specify the template parameters. Why is the compiler not able to deduce this for me? How do I have to change my template definition to make this work?
vector<int> numbers = { 1, 3, 5 };
// vector<string> strings = map(numbers, [] (int x) { return string(x,'X'); });
vector<string> strings = map<int, string>(numbers, [] (int x) { return string(x,'X'); });
Runnable code: http://ideone.com/FjGnxd
The original code in this question comes from here: The std::transform-like function that returns transformed container
Your function expects an std::function argument, but you're calling it with a lambda expression instead. The two are not the same type. A lambda is convertible to std::function, but template argument deduction requires exact matches and user defined conversions are not considered. Hence the deduction failure.
Deduction does work if you actually pass an std::function to map().
std::function<string(int const&)> fn = [] (int x) { return string(x,'X'); };
vector<string> strings = map(numbers, fn);
Live demo
One possible workaround to avoid having to specify the template arguments is to modify the function to accept any kind of callable, rather than an std::function object.
template<typename T, typename Func>
std::vector<typename std::result_of<Func(T)>::type>
map(const std::vector<T> &v, Func f) {
// ...
}
Another version of the same idea, using decltype and declval instead of result_of
template<typename T, typename Func>
std::vector<decltype(std::declval<Func>()(std::declval<T>()))>
map(const std::vector<T> &v, Func f) {
// ...
}
Finally, using a trailing return type
template<typename T, typename Func>
auto map(const std::vector<T> &v, Func f)
-> std::vector<decltype(f(v[0]))> {
// ...
}
Live demo
Suppose, I want to implement a generic higher-order Map function in C++. Map should take a container and a transformation function and return a container of the same type, but possibly with different type of items.
Let's take vector for instance:
template <typename InT, typename OutT, typename Tr>
vector<OutT> Map(vector<InT> cont, Tr tr)
{
OutCont out(cont.size());
auto oit = out.begin();
for (auto it = cont.cbegin(); it != cont.cend(); ++it, ++ot)
{
*oit = tr(*it);
}
}
which I want to use like this:
vector<int> v(10);
std::iota(v.begin(), v.end(), 0);
auto x = Map(v, [](int x) -> int {return x * 2;});
This fails in VC++ 2012, giving me the following error:
error C2783: 'std::vector<OutT> Map(std::vector<_Ty>,Tr)' : could not deduce template argument for 'OutT'
It seems to me that the compiler has all the necessary information, because I explicitly defined the return type in the lambda. Is there a way around this?
The above example uses vector. Is there a way to use a generic type, so that the input and output types are the same? For instance if I have an input container defined as vector<string> and the transformation function tr(string a) -> int, then my goal is to make the compiler to figure out the output type to be vector<int>. Here's the pseudo-code for what I want to achieve:
template <typename Cont<InT>, typename Cont<OutT>, typename Tr<InT, OutT>>
Cont<OutT> Map(Cont<InT> cont, Tr<InT, OutT> tr)
{
// Implementation
}
You may write something like:
template <typename InT, typename Tr>
auto Map(std::vector<InT> cont, Tr tr) -> std::vector<decltype(tr(cont[0]))>
{
std::vector<decltype(tr(cont[0]))> out(cont.size());
auto oit = out.begin();
for (auto it = cont.cbegin(); it != cont.cend(); ++it, ++oit)
{
*oit = tr(*it);
}
return out;
}
Out type is deduced.
[Edit]
For a more generic function with more container:
template <template<typename, typename...> class Container, typename InT, typename Tr, typename... Args>
auto Map(const Container<InT, Args...>& cont, Tr tr) -> Container<decltype(tr(cont[0])), Args...>
{
Container<decltype(tr(cont[0])), Args...> out(cont.size());
auto oit = out.begin();
for (auto it = cont.cbegin(); it != cont.cend(); ++it, ++oit)
{
*oit = tr(*it);
}
return out;
}
Notice the typename... needed because std::vector can also take allocator