алгоритмы 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()})
Это похоже на пустую трату символов и потерю ясности. Я знаю, что я мог бы написать другую функцию/вызываемую и передать указатель функции/вызываемый объект этим функциям алгоритма, но мне часто нужно сделать это только один раз в программе, и это не кажется мне хорошим способом решения проблемы. То, что я хочу сделать, в идеале-это скажите:
auto m = std::max_element(std::begin(vec), std::end(vec), &MyType::val);

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

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

4 ответа:

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

Смотрите жить на Колиру

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

Просто Используйте bind и std::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() невозможна, так как необходимо передать двоичный оператор.

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

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