Передать функцию-член как оператор сравнения для алгоритма стандартной библиотеки C ++ - PullRequest
0 голосов
/ 15 ноября 2018

В моем коде теперь есть что-то вроде

    Foo bar;
    std::unordered_set<Foo>::iterator minElement =
      std::min_element(std::begin(mySet),
                       std::end(mySet),
                       [&bar](Foo const &lhs, Foo const &rhs) {
                         return bar.myWeakLessOperator(lhs, rhs);
                       });

Интересно, существует ли способ упростить его, передавая непосредственно функцию-член myWeakLessOperator (которая не является статической) вместо написания лямбда-выражения?функция просто для вызова.

Я хотел бы получить что-то вроде

    Foo bar;
    std::unordered_set<Foo>::iterator minElement =
      std::min_element(std::begin(mySet),
                       std::end(mySet),
                       /* something that rely to */ bar.myWeakLessOperator);

Любая идея, если это возможно и как это сделать?

Ответы [ 4 ]

0 голосов
/ 15 ноября 2018

Ближайшим к тому, что вы хотите, может быть

auto minElement =
  std::min_element(
            std::begin(mySet),
            std::end(mySet),
            mem_fun_functor(&Foo::myWeakLessOperator) 
  );

. std::mem_fun (устарело в c ++ 11) и std::mem_fn оба оборачивают указатель на функцию-член, хотя оба принимают экземпляр какпараметр для вызова функции-члена.Если вам нужен функтор, который также оборачивает объект, я думаю, что вам нужно написать свой собственный:

auto mem_fun_functor = 
    [&bar](decltype(&Foo::myWeakLessOperator) f){
        return [f,&bar](const Foo& a,const Foo& b) {
                  return (bar.*f)(a,b); 
               };
};

Однако, учитывая, что ни один из ответов не намного короче или не приводит к более чистому коду, я бы подумалпросто использовать свою первую версию с лямбда-выражением (если, возможно, у вас нет множества различных функций-членов, которые вы хотите использовать в качестве компаратора).

Что вы на самом деле подразумеваете под «упрощением»?Вам нужно указать объект, для которого вы хотите вызвать функцию-член, вам нужно указать, как вы хотите пересылать параметры.Это в основном все лямбда делает.

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

В конечном счете это вопрос стиля, и то, что вы считаете читабельным, но возможность объявлять вещи в самом узком объеме - одно из больших преимуществ использования лямбд.

0 голосов
/ 15 ноября 2018

вы можете использовать std::bind или какую-либо другую оболочку .

ПРИМЕР:

using namespace std::placeholders;
Foo bar;
std::unordered_set<Foo>::iterator minElement =
  std::min_element(std::begin(mySet),
                   std::end(mySet),
                   std::bind(&Foo::myWeakLessOperator, bar, _1, _2));

ИЛИ

Foo bar;
std::unordered_set<Foo>::iterator minElement =
  std::min_element(std::begin(mySet),
                   std::end(mySet),
                   gnr::memfun<MEMFUN(Foo::myWeakLessOperator)>(bar));
0 голосов
/ 15 ноября 2018

Возможным решением является использование структуры, удовлетворяющей Сравните внутри Foo:

class Foo 
{
public:
    struct WeakLessOperator
    {
        bool operator()(const Foo& a, const Foo& b)
        {
            // implementation - take care of meeting requirements of Compare
            return true;
        }
    };
    WeakLessOperator myWeakLessOperator;
};

Foo bar;

auto minElement =
    std::min_element(std::begin(mySet),
                     std::end(mySet),
                     bar.myWeakLessOperator);
0 голосов
/ 15 ноября 2018

Итак, вы хотите иметь функциональный объект, который представляет функцию-член, привязанную к конкретному получателю. К сожалению, в стандарте или Boost нет ничего, что могло бы это сделать.

То, что вы можете сделать, это написать свой довольно легко.

template <typename R, typename T>
struct member_function_binder {
    T *receiver;
    R T::*pmf;

    template <typename... Args>
    auto operator()(Args&&... args) {
        return (receiver->*pmf)(std::forward<Args>(args)...);
    }
};

template <typename R, typename T>
auto bind_member_function(R T::*pmf, T &receiver) {
    return member_function_binder<R, T>{&receiver, pmf};
}

Взгляните на live демо , я думаю, это может быть то, что вы хотите.


Еще более кратко, вам не нужно иметь отдельный класс member_function_binder, если вы возвращаете лямбду из bind_member_function примерно так:

template <typename R, typename T>
auto bind_member_function(R T::*pmf, T &receiver) {
    return [pmf, &receiver](auto&&... args) {
        return (receiver.*pmf)(std::forward<decltype(args)>(args)...);
    };
}

Живая демоверсия


Решение для передачи унарной функции-члена, такой как Foo::compareTo(const Foo &rhs), а не того, что запросил OP:

То, что вы хотите, это std::mem_fn; это обертка, которая превращает указатель на функцию-член в объект функции. Вы бы использовали это так:

auto min = std::min_element(
        begin(mySet), end(mySet), std::mem_fn(&Foo::myWeakLessOperator));
...