2011-01-26 4 views
1

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

В качестве иллюстрации приведен ниже код, который сортирует точки, используя различные политики для й и у координат:

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

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

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

Возможно, вам нужна функция 'make_sortXY' для вывода аргументов шаблона? См. 'Make_pair'. Это предполагает, что C++ 0x позволяет безымянный тип лямбды быть аргументом шаблона вообще, я не знаю. –

+1

Также выражение лямбда приводит к * объекту * с помощью 'operator()'. В общем случае для этого требуется экземпляр, потому что там хранятся любые захваченные переменные, и я не уверен, что lambda no-capture определен как особый случай. В частности, имеет ли его тип доступный конструктор no-args, который вы использовали? –

+0

@Steve: спасибо за ваши комментарии. Я понимаю, что вы имеете в виду, кроме последнего предложения во втором комментарии. Что вы имеете в виду? –

ответ

1

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

+0

Да, я это понимаю. Я должен был бы изменить 'SortXY' и добавить конструктор, а затем использовать его как' SortXY (Descending(), Ascending()) '. Это сильно разрушает код (конструктор, переменные-члены в SortXY, ...). Я бы предпочел использовать 'typeof (/ * lambda expression * /) ... –

+1

Вы не можете. У Lambdas есть полностью неопределенные типы - вы не можете даже decltype выражать лямбда. Нет способа получить шаблон типа лямбда без использования вычитания типа на точных лямбда-идентичных определениях, которые не приемлемы. Честно говоря, я признаю, что ваш текущий код проще, но это просто * плохая практика * - как только вы хотите, чтобы какое-то состояние там было, вы ввернуты. Вы только пишете SortXY один раз, но можете называть его десятки раз. – Puppy

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

Здесь мы пользуемся простой ЬурейеЕ для того, чтобы определить указатель на функцию один раз и используйте только его имя, а не повторяя декларацию снова и снова.