web-dev-qa-db-ja.com

std :: pairを返す関数を簡単に処理する方法はありますか?

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()));
33
Adam Hunyadi

これは、ヘルパー関数をプロンプトするのに十分な一般的なケースのように見えます。

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で作成できます

注:SFINAEを適用できるように、C++ 14でもdecltypeの戻り値の型に繰り返されるderefを保持します。

Coliruでライブを参照

13
Quentin

構造化バインディング C++ 17から、直接行うことができます

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

スコープの汚染を回避するために、割り当てをより小さなスコープで囲むことができます。

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);
}();
23
krzaq

標準の現在のリビジョンでは、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のタイプが標準コンテナーの1つになることがわかっている場合は、少しクリーンなタイプ指定decltype(lhs)::iteratorを使用できます。

2
The Vee

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では、実行したことを実行するだけです。十分にきれいにしてください。

C++ 14の実例