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で作成できます 。
注:SFINAEを適用できるように、C++ 14でもdecltype
の戻り値の型に繰り返されるderef
を保持します。
構造化バインディング C++ 17から、直接行うことができます
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);
}();
標準の現在のリビジョンでは、2つの参照を一度に割り当てる方法はありません。 C++ 17とヘルパーテンプレートを必要とするBarryを除いて、他のどの回答もそれを行わないことに注意してください。
ただし、最小要素と最大要素への読み取り/書き込みアクセスが必要な場合は、_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
のタイプが標準コンテナーの1つになることがわかっている場合は、少しクリーンなタイプ指定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では、実行したことを実行するだけです。十分にきれいにしてください。