有没有办法轻松处理返回std :: pairs的函数?

时间:2016-10-27 11:13:26

标签: c++ c++11 tuples std-pair minmax

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

6 个答案:

答案 0 :(得分:32)

使用C ++ 17中的结构化绑定,您可以直接执行

auto [lhsMinIt, lhsMaxIt] = std::minmax_element(lhs.begin(), lhs.end());

答案 1 :(得分:23)

为避免污染范围,您可以将作业括在较小的范围内:

int lhsMin, lhsMax;

{
    auto it = std::minmax_element(lhs.begin(), lhs.end());
    lhsMin = *it.first;
    lhsMax = *it.second;
}

或者,您可以使用lambda

int lhsMin, lhsMax;

std::tie(lhsMin, lhsMax) = [&]{
    auto it = std::minmax_element(lhs.begin(), lhs.end());
    return std::make_tuple(*it.first, *it.second);
}();

答案 2 :(得分:13)

这似乎足以提示辅助函数:

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,但是是一个完整的实现can be made in C++11

注意:即使在C ++ 14中,我也会在decltype的返回类型中保留重复的deref,以便SFINAE可以应用。

See it live on Coliru

答案 3 :(得分:4)

我只是更直接并编写我自己的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()));

此处lohi是对容器本身的引用。

答案 4 :(得分:2)

在标准的当前版本中,无法一次分配两个引用,如果这就是您所追求的。请注意,除了需要C ++ 17和帮助模板的Barry之外,没有其他答案可以做到这一点。

但是,如果您希望对最小和最大元素进行读写访问,为什么不直接使用minmax_element为您提供的迭代器呢?无论如何,它可能会生成与使用引用相同的机器代码,至少如果lhsContiguousContainer,但也可能在其他情况下。

您需要更少依赖自动类型扣除,例如,

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

答案 5 :(得分:2)

在C ++ 14或更高版本中

setVisibility(View.GONE)

所以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) ) )... ); } ); }; } 接受一个函数对象。它返回一个函数对象,当传递一个类似元组时,继续在类元组的每个元素上调用函数对象,并从中生成一个元组。

然后我们编写dereference元组:

fmap_over_tuple

现在在C ++ 17中我们做到了:

auto dereference_tuple = fmap_over_tuple(
  [](auto&& e) { return *e; }
);

鲍勃是你的叔叔。

在C ++ 11中,做你做的。足够干净。

C++14 live example