从lambda表达式创建仿函数

时间:2011-01-26 10:34:14

标签: c++ lambda c++11 functor

我想知道是否可以从lambda表达式创建一个实际的functor对象。我不这么认为,但如果没有,为什么?

为了说明,给出下面的代码,使用x和y坐标的各种策略对点进行排序:

#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"; 
                  });
}

表达式std::sort(pts.begin(), pts.end(), SortXY<Descending, Ascending>());根据x的下降值排序,然后按升序y值排序。这很容易理解,我不确定我是否真的想在这里使用lambda表达式。

但是如果我想用lambda表达式替换Ascending / Descending,你会怎么做?以下有效:

std::sort(pts.begin(), pts.end(), SortXY<
    [](int l, int r) { return l>r; }, 
    [](int l, int r) { return l<r; }
>());

3 个答案:

答案 0 :(得分:1)

出现此问题是因为SortXY只接受类型,而lambdas是对象。您需要重新编写它以便它需要对象,而不仅仅是类型。这是功能对象的基本用法 - 看看std::for_each如何不采用类型,它需要一个对象。

答案 1 :(得分:0)

我发布了一个类似的问题w.r.t.类中的lambda仿函数。 看看这个,也许有帮助:

Lambda expression as member functors in a class

答案 2 :(得分:0)

我遇到了类似的问题:在某些情况下需要提供“原始”功能指针和其他仿函数。所以我想出了一个像这样的“解决方法”:

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);
};

假设你有两个非常简单的函数 - 如上所述 - 是一个仿函数或一个像这样的原始函数指针:

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;
}

然后你可以这样称呼这个函数:

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;

当然在这个例子中没有真正的好处传递int作为参考...它只是一个例子: - )

改进:

您还可以将Selector类修改为更简洁:

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;
};

这里我们利用一个简单的 typedef 来定义函数指针一次,只使用它的名称,而不是一遍又一遍地写声明。