How to make C++11 functions taking function<> parameters accept lambdas automatically

Why would you want to create a dynamic indirection via std::function<...> in the first place? Just templatize on the function object and you are sorted:

template <typename A, typename F> 
auto map(F f, std::vector<A> arr) -> std::vector<decltype(f(arr[0]))> {
    std::vector<decltype(f(arr[0]))> res;
    for (int i=0; i<arr.size(); ++i)
        res.push_back(f(arr[i]));
    return res;
}

In fact, there isn't really any need for nailing the container type either and you probably want to pass it by [const] reference as well:

template <typename C, typename F> 
auto map(F f, C const& c) -> std::vector<decltype(f(*c.begin()))> {
    std::vector<decltype(f(*c.begin()))> res;
    for (auto const& value: c)
        res.push_back(f(value));
    return res;
}

Finally, please note that the standard C++ library already as a "map" function. It just happens to be spelled std::transform() and has an interface which fits the generic approach in C++ better:

std::vector<int> result;
std::transform(a.begin(), a.end(), std::back_inserter(result),
               [](int x){ return x;});

Your map function is broken. Do not use std::function unless you cannot use a template; and in this instance, you most assuredly can. You don't need B as a template parameter because decltype can give it to you, and you don't need the argument type to actually be a std::function at all.

template <typename A, typename F> auto map(F f, vector<A> arr) -> std::vector<decltype(f(arr.front())> {
    std::vector<decltype(f(arr.front())> res;
    for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
    return res;
}

For the record, this is ignoring everything else wrong with your map function.


Finally figured out a generic wrapper function make_function (in current c++11) for converting any lambda to its corresponding std::function object with type deduction. Now instead of using ctor:

map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});

which requires giving the same type information twice, the following succinct form works

map(make_function([](int x) -> int { return x;}),a); //now OK

Code is below:

 #include <vector>
 #include <functional>
 using namespace std;

 template <typename T>
 struct function_traits
    : public function_traits<decltype(&T::operator())>
 {};

 template <typename ClassType, typename ReturnType, typename... Args>
 struct function_traits<ReturnType(ClassType::*)(Args...) const> {
    typedef function<ReturnType (Args...)> f_type;
 };

 template <typename L> 
 typename function_traits<L>::f_type make_function(L l){
   return (typename function_traits<L>::f_type)(l);
 }

 template <typename A,typename B> 
 vector<B> map(std::function<B (A)> f, vector<A> arr) {
       vector<B> res;
       for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
       return res;
}

int main () {
    vector<int> a = {1,2,3};
    map(make_function([](int x) -> int { return x;}),a); //now OK
    return 0;
}

--original answer--

To answer my own question after a couple of weeks' search (and getting chastised for using std::function<> as parameters), probably the best way I can find to have function<>-typed parameters accept lambda's (in c++11) is simply via explicit cast:

map((function<int (int)>) ([](int x) -> int { return x;} ), {1,2,3});

Or using ctor:

map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});

For comparison, if you have a function taking std::string (e.g. void ff(string s) {...}), it can take const char* automatically. (ff("Hi") would work). The automatic conversion from lambda to std::function<> does not similarly work in c++11 (, which is unfortunate, IMO).

Hopefully, things will improve in c++14/1y when lambdas can be properly typed or better type-deduced.

Tags:

C++

Lambda

C++11