以指向成员的指针作为比较器/"key"的 STD 算法

std algorithms with pointer to member as comparator/"key"

本文关键字:key 算法 STD 比较器 成员 指针      更新时间:2023-10-16

我经常发现自己使用std::sort, std::max_element等简单调用成员函数的lambda

std::vector<MyType> vec;
// populate...
auto m = std::max_element(std::begin(vec), std::end(vec),
    [](const MyType& a, const MyType& b) { return a.val() < b.val()})

这感觉像是在浪费字符和失去清晰度。我知道我可以编写另一个函数/可调用对象,并将函数指针/可调用对象传递给这些算法函数,但我通常只需要在程序中进行一次排序,这并不是解决问题的好方法。理想情况下,我想做的是:

auto m = std::max_element(std::begin(vec), std::end(vec), &MyType::val);

,并根据其val() s对对象进行排序。我忽略了标准库的某些部分,可以帮助我做到这一点吗?或者其他简单的方法?我想让排序和搜索的依据尽可能明显。

我知道,只是&MyType::val是不够的,我正在寻找的东西,也许可以包装它,或提供类似的功能,而不模糊的意义。

您可以使用std::mem_fn(或std::tr1::mem_fn)

int main()
{
    std::vector<MyType> vec;
    auto m = std::max_element(std::begin(vec), std::end(vec), compare_by(std::mem_fn(&MyType::field)));
}

当然,这假设您的工具箱中有一个像compare_by这样的实用程序(您应该:)):

template <typename F>
struct CompareBy {
    explicit CompareBy(F&& f) : f(std::forward<F>(f)) {}
    template <typename U, typename V> 
        bool  operator()(U const& u, V const& v) const {
            return f(u) < f(v);
        }
private:
    F f;
};
template <typename F>
CompareBy<F> compare_by(F&& f) { return CompareBy<F>(std::forward<F>(f)); }

查看Live On Coliru

您可以在不引入任何新函数(无论是否模板化)的情况下实现

只使用bindstd::less

auto m = std::max_element(vec.begin(), vec.end(), 
    bind(less<>(), bind(&MyType::val, _1), bind(&MyType::val, _2)));

模板比较器可以帮助您:

template <typename StructureType,
          typename MemberType,
          MemberType StructureType::*member>
bool comparator(const StructureType& the_first, const StructureType& the_second)
{
  return the_first.*member < the_second.*member;
}
http://ideone.com/K8ytav

一点类型特征的魔力当然可以让你避免写类型

如何一次重载operator<为您的自定义类型?这可以自然地在类内部(或直接在类旁边)完成,然后在迭代器旁边不需要进一步的参数。

传递val()函数是不可能的,因为必须传递一个二进制运算符。

编辑:在阅读了其他有价值的替代方案(也是sehe的好回应)之后,我想确认我在下面的评论中已经提到的:在我看来,没有什么比lambda表达式的可读性,局部性和灵活性更好的了(-冒着写两次段落的风险)。

@Ryan Haining:我建议你保持原封不动

为了避免使用std::mem_fn,对这个答案进行改进,可以为compare_by函数提供一个指向成员重载的指针。

范例用法
std::sort(std::begin(vec), std::end(vec), compare_by(&MyType::field));
std::sort(std::begin(vec), std::end(vec), compare_by(&MyType::field, std::greater<>{}));

实现

的代码
#include <functional> // std::less
#include <utility> // std::move
#include <type_traits> // std::is_invocable_r
// Forward declaration
template<typename R, typename T, typename F = std::less<R>>
auto compare_by(R T::*, F = F{});
// Implementation
namespace detail {
template<typename T, typename F>
struct compare_by_t;
template<typename R, typename T, typename F>
struct compare_by_t<R T::*, F> : private F
{
    compare_by_t(F&& f, R T::*m): F{std::move(f)}, _member{m} {}
    R T::* _member;
    bool operator()(T const& x, T const& y) const
    {
        return F::operator()(x .* _member, y .* _member);
    }
};
} // detail
template<typename R, typename T, typename F>
auto compare_by(R T::* member, F f)
{
    static_assert(std::is_invocable_r<bool, F, R, R>::value);
    return detail::compare_by_t<R T::*, F>{ std::move(f), member };
}