【问题标题】:Creating functor from lambda expression从 lambda 表达式创建仿函数
【发布时间】:2011-01-26 10:34:13
【问题描述】:

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

为了说明,给定下面的代码,它使用 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&lt;Descending, Ascending&gt;()); 按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; }
>());

【问题讨论】:

  • 可能你需要一个make_sortXY 函数来推断模板参数?见make_pair。这是假设 C++0x 完全允许 ​​lambda 的无名类型作为模板参数,我不知道。
  • 同样,一个 lambda 表达式会产生一个带有 operator()object。一般来说,它需要实例,因为这是保留任何捕获的变量的地方,而且我不确定是否以任何方式将无捕获 lambda 定义为特殊情况。特别是,它的类型是否具有您使用过的可访问的无参数构造函数?
  • @Steve:感谢您的 cmets。我明白你的意思,除了你第二条评论中的最后一句话。你指的是什么?
  • 当您编写XOrder()(lhs.x, rhs.x) 时,您正在调用XOrder 的无参数构造函数(嗯,或者其他参数都是可选的构造函数),然后在生成的临时函数上调用operator()目的。我不知道,因为我没有查过,你是否可以从它的类型中实例化一个这样的 lambda。
  • @Steve:我猜不是,因为你无法获得 lambda 的类型。

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


【解决方案1】:

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

【讨论】:

  • 是的,我意识到这一点。我将不得不更改SortXY 并添加一个构造函数,然后将其用作SortXY(Descending(), Ascending())。这使代码变得非常安静(构造函数,SortXY 中的成员变量,...)。我宁愿使用 `typeof(/* lambda 表达式 */)...
  • 你不能。 Lambda 有完全未定义的类型——你甚至不能 decltype 一个 lambda 表达式。如果不对确切的 lambda 进行类型推导,就无法获得 lambda 的类型,这是不可接受的。坦率地说,我承认您当前的代码更容易,但这只是 不好的做法 - 只要您想要其中的任何状态,那么您就完蛋了。你只写一次 SortXY,但你可能会调用它几十次。
【解决方案2】:

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

Lambda expression as member functors in a class

【讨论】:

    【解决方案3】:

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

    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 来定义函数指针并只使用它的名称,而不是一遍又一遍地编写声明。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多