C ++ 11 имеет функцию std::minmax_element
который возвращает пару значений. Это, однако, довольно запутанно для обработки и чтения, и создает дополнительную, впоследствии бесполезную переменную для загрязнения области.
auto lhsMinmax = std::minmax_element(lhs.begin(), lhs.end());
int &lhsMin = *(lhsMinMax.first);
int &lhsMax = *(lhsMinmax.second);
Есть лучший способ сделать это? Что-то вроде:
int lhsMin;
int lhsMax;
std::make_pair<int&, int&>(lhsMin, lhsMax).swap(
std::minmax_element(lhs.begin(), lhs.end()));
Это выглядит достаточно распространенным случаем, чтобы запросить вспомогательную функцию:
template <class T, std::size_t...Idx>
auto deref_impl(T &&tuple, std::index_sequence<Idx...>) {
return std::tuple<decltype(*std::get<Idx>(std::forward<T>(tuple)))...>(*std::get<Idx>(std::forward<T>(tuple))...);
}
template <class T>
auto deref(T &&tuple)
-> decltype(deref_impl(std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{})) {
return deref_impl(std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::remove_reference_t<T>>::value>{});
}
// ...
int lhsMin;
int lhsMax;
std::tie(lhsMin,lhsMax) = deref(std::minmax_element(lhs.begin(), lhs.end()));
index_sequence
это C ++ 14, но полная реализация может быть сделано в C ++ 11.
Примечание: я бы сохранил повторение decltype
в deref
тип возврата даже в C ++ 14, так что SFINAE может применяться.
С структурированное связывание из C ++ 1z вы можете напрямую
auto [lhsMinIt, lhsMaxIt] = std::minmax_element(lhs.begin(), lhs.end());
Чтобы избежать загрязнения вашей области, вы можете заключить назначение в меньшую область:
int lhsMin, lhsMax;
{
auto it = std::minmax_element(lhs.begin(), lhs.end());
lhsMin = *it.first;
lhsMax = *it.second;
}
В качестве альтернативы вы можете использовать лямбду
int lhsMin, lhsMax;
std::tie(lhsMin, lhsMax) = [&]{
auto it = std::minmax_element(lhs.begin(), lhs.end());
return std::make_tuple(*it.first, *it.second);
}();
Я просто был бы более прямым и написал бы свою собственную версию minmax_element
:
template <class Iter, class R = typename iterator_traits<Iter>::reference>
std::pair<R,R> deref_minmax(Iter first, Iter last)
{
auto iters = std::minmax_element(first, last);
return std::pair<R,R>{*iters.first, *iters.second};
}
Что тогда просто:
int lo, hi;
std::tie(lo, hi) = deref_minmax(lhs.begin(), lhs.end());
Это ограничило бы вас только одной копией элементов (что не так уж важно для int
s), также позволяет вам поддерживать доступ к ссылкам в реальном контейнере.
В C ++ 17 для забавы мы могли бы написать обобщенный разыменование:
template <class Tuple>
auto deref(Tuple&& tup) {
return std::apply([](auto... args) {
return std::tuple <decltype(*args)...>(*args...);
}, tup);
}
auto& [lo, hi] = deref(std::minmax_element(lhs.begin(), lhs.end()));
Вот lo
а также hi
ссылки в сам контейнер.
Там нет никакого способа назначить два Рекомендации сразу в текущей редакции стандарта, если это то, что вы после. Обратите внимание, что ни один из других ответов не делает этого, кроме Барри, который требует C ++ 17 и вспомогательный шаблон.
Однако, если вам нужен доступ на чтение и запись к вашим минимальным и максимальным элементам, почему бы просто не пойти с итераторами minmax_element
предоставляет вам напрямую? В любом случае, вероятно, будет сгенерирован идентичный машинный код с использованием ссылок, по крайней мере, если ваш lhs
это ContiguousContainer
но может быть и в других случаях.
Вам нужно будет немного меньше полагаться на автоматическое удержание типа, например:
decltype(lhs.begin()) lhsMinIt, lhsMaxIt;
std::tie(lhsMinIt, lhsMaxIt) = std::minmax_element(lhs.begin(), lhs.end());
/* now access your minimum and maximum as *lhsMinIt and *lhsMaxIt */
Если вы знаете тип lhs
будет одним из стандартных контейнеров, вы можете использовать немного более чистое обозначение типа decltype(lhs)::iterator
,
В C ++ 14 или выше
template<class=void, std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
return [](auto&&f){
return f( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto indexer() {
return indexer( std::make_index_sequence<N>{} );
}
template<class F>
auto fmap_over_tuple( F&& f ) {
return [f=std::forward<F>(f)](auto&& tuple) {
using Tuple = decltype(tuple);
using Tuple_d = std::decay_t<Tuple>;
auto index = indexer< std::tuple_size< Tuple_d >::value >();
return index(
[&f, &tuple](auto&&...Is) {
using std::get;
return std::make_tuple(
f( get<Is>( std::forward<Tuple>(tuple) ) )...
);
}
);
};
}
так fmap_over_tuple
принимает функциональный объект. Он возвращает объект функции, который при передаче в виде кортежа продолжает вызывать объект функции в каждом элементе, подобном кортежу, и генерирует из него кортеж.
Затем мы пишем кортеж разыменования:
auto dereference_tuple = fmap_over_tuple(
[](auto&& e) { return *e; }
);
Теперь в C ++ 17 мы делаем:
auto[Min, Max] = dereference_tuple( std::minmax_element(lhs.begin(), lhs.end() );
а боб твой дядя
В C ++ 11 просто делай то, что делал ты. Достаточно чисто.