Подтвердить что ты не робот

Std с указателем на член как компаратор/ "ключ"

Я часто использую std::sort, std::max_element и т.д. с помощью лямбды, которая просто вызывает функцию-член

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

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

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

и отсортировать объекты по их val() s. Есть ли какая-то часть stdlib, которую я пропускаю, что могло бы помочь мне в этом? или другой простой способ сделать это? Я хотел бы сделать, что это сортировка или поиск, насколько это возможно.

Я знаю, что просто &MyType::val недостаточно, я ищу что-то, что может его обернуть, или предоставить аналогичную функциональность, не обманывая смысл.

4b9b3361

Ответ 1

Вы можете использовать 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

Ответ 2

Вы можете сделать это без, введя любые новые функции (шаблонные или нет).

Просто используйте bind и std::less

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

Ответ 3

Шаблонный компаратор может помочь вам:

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

Немного волшебства черт типа, безусловно, позволит вам избежать написания типа.

Ответ 4

Как насчет перегрузки operator< для вашего пользовательского типа? Это можно сделать естественным образом внутри класса (или непосредственно рядом с ним), а затем никаких дальнейших аргументов не требуется рядом с итераторами.

Передача вашей функции val() невозможна, так как вы должны передать двоичный оператор.

РЕДАКТИРОВАТЬ: прочитав другие ценные альтернативы (также отличный ответ), я хочу подтвердить, что я уже упомянул в комментарии ниже: По-моему, ничто не сравнивает читаемость, локальность, а также гибкость выражения лямбды (- - рискуя написать несколько отрывков дважды).

@Ryan Haining: Я предлагаю вам сохранить его как в своем оригинальном посте.

Ответ 5

Улучшение ответа sehes, чтобы избежать необходимости использовать 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 };
}