1

I would like to know if it is possible to create an actual functor object from a lambda expression. I don't think so, but if not, why?

To illustrate, given the code below, which sorts points using various policies for x and y coordinates:

#include <vector>
#include <functional>
#include <algorithm>
#include <iostream>

struct Point 
{ 
    Point(int x, int y) : x(x), y(y) {}
    int x, y; 
};

template <class XOrder, class YOrder> 
struct SortXY : 
    std::binary_function<const Point&, const Point&, bool>
{
    bool operator()(const Point& lhs, const Point& rhs) const 
    {
        if (XOrder()(lhs.x, rhs.x))
            return true;
        else if (XOrder()(rhs.x, lhs.x))
            return false;
        else
            return YOrder()(lhs.y, rhs.y);
    }          
};

struct Ascending  { bool operator()(int l, int r) const { return l<r; } };
struct Descending { bool operator()(int l, int r) const { return l>r; } };

int main()
{
    // fill vector with data
    std::vector<Point> pts;
    pts.push_back(Point(10, 20));
    pts.push_back(Point(20,  5));
    pts.push_back(Point( 5,  0));
    pts.push_back(Point(10, 30));

    // sort array
    std::sort(pts.begin(), pts.end(), SortXY<Descending, Ascending>());

    // dump content
    std::for_each(pts.begin(), pts.end(), 
                  [](const Point& p) 
                  {
                     std::cout << p.x << "," << p.y << "\n"; 
                  });
}

The expression std::sort(pts.begin(), pts.end(), SortXY<Descending, Ascending>()); sorts according to descending x values, and then to ascending y values. It's easily understandable, and I'm not sure I really want to make use of lambda expressions here.

But if I wanted to replace Ascending / Descending by lambda expressions, how would you do it? The following isn't valid:

std::sort(pts.begin(), pts.end(), SortXY<
    [](int l, int r) { return l>r; }, 
    [](int l, int r) { return l<r; }
>());
Daniel Gehriger
  • 7,339
  • 2
  • 34
  • 55
  • 2
    Possibly you need a `make_sortXY` function to deduce the template arguments? See `make_pair`. This is assuming that C++0x permits the nameless type of a lambda to be a template argument at all, I don't know. – Steve Jessop Jan 26 '11 at 10:40
  • 1
    Also a lambda expression results in an *object* with an `operator()`. In general it requires the instance, because that's where any captured variables are kept, and I'm not sure whether a no-capture lambda is defined as a special case in any way. In particular, does its type have an accessible no-args constructor, that you've used? – Steve Jessop Jan 26 '11 at 10:44
  • @Steve: thank's for your comments. I understand what you mean, except the last sentence in your 2nd comment. What are you referring to? – Daniel Gehriger Jan 26 '11 at 12:16
  • when you write `XOrder()(lhs.x, rhs.x)`, you are calling the no-args constructor of `XOrder` (well, or another constructor whose parameters are all optional), and then calling `operator()` on the resulting temporary object. I don't know, because I haven't looked it up, whether you can just instantiate a lambda like that from its type. – Steve Jessop Jan 26 '11 at 13:40
  • @Steve: I guess not, since you can't get the type of a lambda. – Daniel Gehriger Jan 26 '11 at 14:00

3 Answers3

1

This problem arises because SortXY only takes types, whereas lambdas are objects. You need to re-write it so that it takes objects, not just types. This is basic use of functional objects- see how std::for_each doesn't take a type, it takes an object.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Yes, I realize that. I would have to change `SortXY` and add a constructor, and then use it as `SortXY(Descending(), Ascending())`. This blows up the code quiet a lot (constructor, member variables in SortXY, ...). I'd rather use a `typeof(/* lambda expression */)... – Daniel Gehriger Jan 26 '11 at 10:46
  • 1
    You can't. Lambdas have totally undefined types- you can't even decltype a lambda expression. There's no way to get template the type of a lambda without using type deduction on that exact lambda- identical definitions not acceptable. Frankly, I admit that your current code is easier, but it's just *bad practice*- as soon as you want any state in there then you're screwed. You only write SortXY once, but you may call it dozens of times. – Puppy Jan 26 '11 at 11:02
0

I have posted a similar question w.r.t. lambda functors within classes. Check this out, perhaps it helps:

Lambda expression as member functors in a class

Community
  • 1
  • 1
gilgamash
  • 862
  • 10
  • 31
0

I had a similar problem: It was required to provide in some cases a "raw"-function pointer and in other a functor. So I came up with a "workaround" like this:

template<class T>
class Selector
{
public:
    Selector(int (*theSelector)(T& l, T& r))
        : selector(theSelector) {}

    virtual int operator()(T& l, T& r) {
        return selector(l, r);
    }

    int (*getRawSelector() const)(T&, T&) {
        return this->selector;
    }

private:
    int(*selector)(T& l, T& r);
};

Assuming you have two very simple functions taking --- as described --- either a functor or a raw function pointer like this:

int
findMinWithFunctor(int* array, int size, Selector<int> selector)
{
    if (array && size > 0) {
        int min = array[0];
        for (int i = 0; i < size; i++) {
            if (selector(array[i], min) < 0) {
                min = array[i];
            }
        }
        return min;
    }
    return -1;
}

int 
findMinWithFunctionPointer(int* array, int size, int(*selector)(int&, int&))
{
    if (array && size > 0) {
        int min = array[0];
        for (int i = 0; i < size; i++) {
            if (selector(array[i], min) < 0) {
                min = array[i];
            }
        }
        return min;
    }
    return -1;
}

Then you would call this functions like this:

int numbers[3] = { 4, 2, 99 };

cout << "The min with functor is:" << findMinWithFunctor(numbers, 3, Selector<int>([](int& l, int& r) -> int {return (l > r ? 1 : (r > l ? -1 : 0)); })) << endl;


// or with the plain version
cout << "The min with raw fn-pointer is:" << findMinWithFunctionPointer(numbers, 3, Selector<int>([](int& l, int& r) -> int {return (l > r ? 1 : (r > l ? -1 : 0)); }).getRawSelector()) << endl;

Of course in this example there is no real benefit passing the int's as reference...it's just an example :-)

Improvements:

You can also modify the Selector class to be more concise like this:

template<class T>
class Selector
{
public:

    typedef int(*selector_fn)(T& l, T& r);

    Selector(selector_fn theSelector)
        : selector(theSelector) {}

    virtual int operator()(T& l, T& r) {
        return selector(l, r);
    }

    selector_fn getRawSelector() {
        return this->selector;
    }

private:
    selector_fn selector;
};

Here we are taking advantage of a simple typedef in order to define the function pointer once and use only it's name rather then writing the declaration over and over.

Alessandro Giusa
  • 1,660
  • 15
  • 11