3

I'm playing around with lambda functions in gcc 4.6.2, and would like to implement a templated "map" function like this:

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, const std::function<B(A)> f) {
  std::vector<B> rv;
  rv.resize(orig.size());
  std::transform(begin(orig), end(orig), begin(rv), f);
  return rv;
}

This doesn't work, because the test code:

int main(int argc, char **argv) {
  std::vector<int> list;
  list.push_back(10);
  list.push_back(20);
  list.push_back(50);

  std::vector<int> transformed = map(list, [](int x) -> int { return x + 1; });
  std::for_each(begin(transformed), end(transformed), [](int x) { printf("-> %d\n", x); });
  return 0;
}

gives this error:

test.cpp:49:80: error: no matching function for call to ‘map(std::vector<int>&, main(int, char**)::<lambda(int)>)’
test.cpp:49:80: note: candidate is:
test.cpp:6:49: note: template<class A, class B> std::vector<B> map(const std::vector<A>&, std::function<B(A)>)

If I remove the templating, and use a vector directly, it compiles fine:

std::vector<int> map(const std::vector<int>& orig, const std::function<int(int)> f) {
  std::vector<int> rv;
  rv.resize(orig.size());
  std::transform(begin(orig), end(orig), begin(rv), f);
  return rv;
}

so it must be a problem with the way I'm defining the template.

Has anyone run into this before? I know lambdas are incredibly new.

Robey Pointer
  • 231
  • 2
  • 7
  • You do know that in the examples in your post, you define the function `map` while calling `map_`? Note the underscore in the call... :-) – Some programmer dude Oct 31 '11 at 07:32
  • Sorry about the typos -- I was pasting this from a file with a dozen different attempts, to see if I could get the compiler to give me a better clue about what it didn't like. Think I've fixed those now. – Robey Pointer Oct 31 '11 at 17:44

3 Answers3

2

You don't need to use std::function. Just make the predicate parameter a template value. For example,

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, B f) {

std::function<> is more useful as a member value type or for defining non-templated code.

Sean Middleditch
  • 2,517
  • 18
  • 31
  • 1
    Assuming you meant to add a new "typename C" to the template, and make the parameter "C f", that has the same problem. It doesn't think the "map" function matches. – Robey Pointer Oct 31 '11 at 07:21
0

I'm tackling with lambdas too and i noticed that you can declare a function pointer in a function definition's parameter list and when you make a call to that function you can pass a lambda expression as an argument if it matches the function prototype of course.

#include <iostream>

#include <vector>

#include <algorithm>

#include <iterator>



template <typename T,typename C>

struct map  {

   typedef C (*F)(const T&);

   std::vector<C> rv;

   map () {}

   map (const std::vector<T>& o,F f)  {

      rv.resize(o.size());

      std::transform (o.begin(),o.end(),rv.begin(),f);

    }

   ~map () {}

   operator std::vector<C> () const  {

      return rv;

    }

 };



int main ()  {

   std::vector<int> asd(5,12);

   std::vector<char> transformed=map<int,char>(asd,[](const int& x)->char {return x+1;});

   std::copy (transformed.begin(),transformed.end(),std::ostream_iterator<int>(std::cout," "));

 }
Marius Pirvu
  • 45
  • 1
  • 1
  • 9
0

The problem is that the compiler can't figure out what to use for B. In order to determine that type it wants to use the function<> you pass in for f, but you don't pass an std::function<> directly. You pass in something you expect to be used to construct a function<>. And in order to do that implicit construction it needs to know the type of argument. So you've got this circular dependency where the type of argument depends on what you pass in, but what gets passed in depends on the type of argument.

You can break this circular dependency by specifying the template parameters, such as map_<int,int>(list, [](int x) -> char { return x + 1; });

(although I see the functor actually returns a char, not an int, so if the type deduction worked for you here you'd be getting back a vector<char> which cannot be converted to a vector<int> when you assign the result to transformed)

However as has been pointed out, generally templates take functors as just a plain template type:

template<typename A,typename Func>
auto map_(const std::vector<A>& orig, Func f) -> std::vector<decltype(f(A()))> {
    std::vector<decltype(f(A()))> rv;
    /*...*/
}

(we use the trailing return type because we need to use the expression f in the return type, which isn't available unless the return type comes afterwards.)

This allows the template to deduce the functor type directly and avoids any type conversions and best allows for optimization.

It's also customary to use iterators as arguments on these sorts of functions, in which case your function is just a wrapper around std::transform, so you can just use that directly. I'm not sure there's a whole lot of value in a special version that deals with vectors specifically.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • That works! (I fixed the "char" thing; sorry about that.) So it sounds like I was missing 2 points: 1. Lambdas aren't of type function, but are converted using an implicit constructor. 2. The only real way to mark the type of a lambda is with decltype, and lambdas don't really have any other way to be described as a type. Agreed about the pointless specificness of the example -- I just wanted to see if it could be done at all, and it can! Thanks for your help! – Robey Pointer Oct 31 '11 at 17:46
  • The answer to this question has a type trait that uses variadic templates and which makes it easier to ask about the lambda's types. For example instead of my use of decltype you could use this to say `function_traits::result_type`. http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda – bames53 Oct 31 '11 at 18:46
  • Also my decltype doesn't work if A isn't default constructible. `decltype(f(orig[0]))` is an alternative. – bames53 Oct 31 '11 at 18:47