C ++ 11の範囲ベースのforループをもう少し便利にする

C ++ 11は素晴らしいです。おそらく(私の意見では)最も美しい機能の1つは、いわゆる範囲ベースのforループです。

for ( std::size_t i(0); i < range.size(); ++i ) { // do something to range[i]; } 

または

for ( Range::iterator it(range.begin()); it != range.end(); ++it ) { // do something to *it; } 

の代わりに、またはC ++ 11自動で簡略化:

for ( auto it(range.begin()); it != range.end(); ++it ) { // do something to *it; } 

これは次のように言えます:

for ( auto& entry : range ) { // do something with entry. } 

これは非常に表現力豊かです:話します範囲内のi番目の位置やエントリを指すイテレータではなく、エントリについて。ただし、この構文には、サブ範囲を持つ機能がありません(たとえば、最後のエントリを無視します)。一連の小さなヘルパー構造体/メソッドで、この機能をクリーンな方法で追加したいと思います。

モチベーションはこれだけです;-)さて、実際の取引です。

この投稿では、rangeのエントリの条件について説明します。基本的に、ヘルパーコードは

for ( auto& entry : range ) { if ( condition(entry) ) { // do something to entry. } } 

と同等の機能を実行する必要がありますが、そのレベルのインデントはありません。

ConditionalRangeのコードは次のとおりです。

template <typename Range, typename Runnable> ConditionalRange<Range, Runnable> makeConditionalRange(Range& range, Runnable&& condition) { static_assert(std::is_same<decltype(condition(*std::declval<Range>().begin())), bool>::value, "Condition must return a boolean value."); return ConditionalRange<Range, Runnable>(range, std::forward<Runnable>(condition)); } template <typename Range, typename Runnable> struct ConditionalRange { public: friend ConditionalRange makeConditionalRange<>(Range&, Runnable&&); public: using iterator_type = ConditionalIterator<decltype(std::declval<Range>().begin()), Runnable>; iterator_type begin() const { auto b = range.begin(); while ( b != range.end() && !condition(*b) ) { ++b; } return ConditionalIterator<decltype(std::declval<Range>().begin()), Runnable>(b, range.end(), condition); } iterator_type end() const { return ConditionalIterator<decltype(std::declval<Range>().begin()), Runnable>(range.end(), range.end(), condition); } private: ConditionalRange(Range& range_, Runnable&& condition_) : range(range_), condition(std::forward<Runnable>(condition_)) { } private: Range& range; Runnable condition; }; 

これはイテレータのヘルパー構造体です:

template <typename Iterator, typename Runnable> struct ConditionalIterator { private: Iterator iterator; Iterator end; const Runnable& condition; public: ConditionalIterator(Iterator b, Iterator e, const Runnable& r) : iterator(b), end(e), condition(r) { } auto operator*() -> decltype(*iterator) { return *iterator; } ConditionalIterator& operator++() { do { ++iterator; } while ( iterator != end && !condition(*iterator) ); return *this; } bool operator==(const ConditionalIterator& second) { return iterator == second.iterator; } bool operator!=(const ConditionalIterator& second) { return !(*this == second); } }; 

使用目的は:

std::vector<int> ns{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for ( const auto& n : makeConditionalRange(ns, [](int a) { return a % 2 == 0; }) ) { std::cout << n << " "; } std::cout << "\n"; 

デモ:ここをクリックしてください!

内容私のアプローチの弱点はありますか?完璧な世界ではいけないのに、どのような議論で失敗するのでしょうか?

コメント

  • 弱点はわかりませんが、私は"条件付き範囲の作成"ではなく、クライアントコードの外観に焦点を当てた名前でmakeConditionalRangeの名前を変更することを検討する必要があると思います。つまり、クライアントコードは次のようになります。for(const auto& n: iterate_if(ns, [](int a) { return a % 2 == 0; }) )
  • ここでの意図は、Boost filter_iterator
  • @JerryCoffinいいえそれはありません' t。私は単に' Boostを調べました;-)
  • イテレータの最も一般的なテストoperator!=これをoperator==
  • @stefan:率直に言って、'はばかげています。' tは標準的なメーカーから車を購入しません海藻が汚染されないことを保証するために、自分で海藻から流れ出るものを作りたいからです。問題は、ライブラリを見る目が1つしかないため、ライブラリのセットがブーストよりもはるかにバグが多いことです。 Boostには、コード修正をチェックおよび検証し、正しいイディオムが正しく使用されていることを確認するためのフィードバックを提供する何千人もの人々がいます。読む: stackoverflow.com/a/149305/14065

回答

中括弧を使用して関数から戻ることができます。そうすれば、長くて面倒なタイプ(DRY、可能な限り)を繰り返す必要がなくなります。たとえば、ConditionalRange::end

iterator_type end() const { return { range.end(), range.end(), condition }; } 

また、関数がCスタイルの配列も受け入れるようにしたい場合があります。したがって、std::beginstd::endメンバー関数を呼び出す代わりに:

iterator_type end() const { return { std::end(range), std::end(range), condition }; } 

これ(コード内のすべての場所)といくつかの些細な変更により、コードはC-でも機能します。スタイル配列(ここでテスト済みで正常に動作します)。

関数の名前付け

名前makeConditionalRangeはかなり長いです。作成した関数は、filterという名前で他のライブラリやプログラミング言語(Pythonが思い浮かびます)にすでに存在します。検討する必要があります。多くのユーザーが一目で名前を認識できるように名前を変更します(さらに、名前は短くなります)。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です