将成员函数作为比较运算符传递给C ++标准库算法

时间:2018-11-15 17:45:45

标签: c++ c++11 standard-library

在我的代码中,我现在有类似的东西

    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(不是静态的)而不是编写用于调用的lambda函数来简化它的方法。

我想获得类似的东西

    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 :(得分:2)

因此,您需要一个函数对象,该对象表示绑定到特定接收者的成员函数。不幸的是,我在标准或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 demo,我想这可能就是您想要的。


更简洁的是,如果您像这样从member_function_binder返回一个lambda,则不需要单独的类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)...);
    };
}

Live demo


传递像Foo::compareTo(const Foo &rhs)这样的一元成员函数而不是OP要求的解决方案:

您想要的是std::mem_fn;它是使成员函数指针变成函数对象的包装器。您将这样使用它:

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

答案 1 :(得分:2)

一种可能的解决方案是在Foo中使用满足Compare的结构:

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

答案 2 :(得分:1)

您可以使用std::bind或其他wrapper

示例:

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

OR

Foo bar;
std::unordered_set<Foo>::iterator minElement =
  std::min_element(std::begin(mySet),
                   std::end(mySet),
                   gnr::memfun<MEMFUN(Foo::myWeakLessOperator)>(bar));

答案 3 :(得分:0)

最接近您想要的是

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

但是,鉴于所有答案都不是那么短,也不会使代码更简洁,因此,我考虑将第一个版本与lambda一起使用(除非您可能有许多要用作比较器的成员函数)

“简化”实际上是什么意思?您确实需要指定要在其上调用成员函数的对象,还需要指定要如何转发参数。那基本上就是lambda所做的所有事情。

例如,将所有这些操作都交给函子可以使您的代码更复杂而不是更简单。在您的第一个摘录中,熟悉标准算法的任何人都可以查看那几行代码,完全了解正在发生的事情。

最终这是样式问题,您认为可读性强,但是能够在最窄的范围内声明内容是使用lambda的一大优势。