Создание функтора из лямбда-выражения - PullRequest
1 голос
/ 26 января 2011

Я хотел бы знать, возможно ли создать реальный объект функтора из лямбда-выражения. Я так не думаю, но если нет, то почему?

Для иллюстрации, с помощью приведенного ниже кода, который сортирует точки, используя различные политики для координат 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. Это легко понять, и я не уверен, что действительно хочу использовать здесь лямбда-выражения.

Но если бы я захотел заменить Ascending / Descending на лямбда-выражения, как бы вы это сделали? Следующие не действительны:

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

Ответы [ 3 ]

1 голос
/ 26 января 2011

Эта проблема возникает из-за того, что SortXY принимает только типы, тогда как лямбды-объекты являются объектами. Вам нужно переписать его так, чтобы он принимал объекты, а не только типы. Это базовое использование функциональных объектов - посмотрите, как std::for_each не принимает тип, а принимает объект.

0 голосов
/ 19 июля 2017

У меня была похожая проблема: в некоторых случаях требовалось указатель «сырой» функции, а в другом - функтор.Поэтому я нашел «обходной путь», подобный следующему:

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 чтобы определить указатель на функцию один раз и использовать только его имя, а не писать объявление снова и снова.

0 голосов
/ 14 апреля 2015

Я опубликовал аналогичный вопрос в отношении лямбда-функторов в классах.Проверьте это, возможно, это поможет:

Лямбда-выражение как функторы-члены в классе

...