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
недостаточно, я ищу что-то, что может его обернуть, или предоставить аналогичную функциональность, не обманывая смысл.
Ответы
Ответ 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 };
}