以下に表示されているのは、プロジェクトの複雑さを克服するための試みであり、コードの主観的な美しさに対する強い欲求の下で行われたものです。
たとえば、タスクは要素を持つコンテナをウォークスルーすることです。 この場合、当然、プログラムの動作は、値に応じて要素ごとに異なります。 かんたん 複雑に思えますが、文字列の通常のベクトルでこの状況を見てみましょう。 このタスクを実行する関数は次のとおりです。
void Action(std::vector<std::string>::const_iterator curr, const std::vector<std::string>::const_iterator &last) { if (curr == last) { return; } // ... }
しかし、覚えているように、現在の要素の特定の値を使用すると、動作が変更されます。 このような値は多数存在するため、異なる動作のバリアントが存在するため、ループ内でif-elseキャンバスを生成するのではなく、別個の関数を作成します。 以下よりも簡単なこと:
void ActionTwo(std::vector<std::string>::const_iterator curr, const std::vector<std::string>::const_iterator &last) { if (curr == last) { return; } // ... } void Action(std::vector<std::string>::const_iterator curr, const std::vector<std::string>::const_iterator &last) { if (curr == last) { return; } // ... if (*curr == ANY_VALUE) { ActionTwo(curr + 1, last); } // ... }
そして、現在の位置の別の値のために、別の動作オプションを実装する必要があります。 続行できないと思うので、これが何につながるかは明らかです。 常に現在の位置と最後の位置を転送する必要があります。 現在の値を後者と比較する必要があるため、クラス関数の共通関数を作成しても問題は解決しません。つまり、std :: vector <std :: string> :: const_iterator&lastを別の定数として作成する必要があります。
そして、LINQとJavaで列挙子イテレーターがどのように便利に作成されたかを思い出しました。この場合、ポインター計算が不要であるため、std :: vector <std :: string> :: const_iterator currの+ =および-=演算子の使用です。 必要なのは、値に応じて動作を変更することにより、コンテナを簡単にバイパスできることだけです。 次に、C ++でのLINQのような列挙子用の自転車を見ていきます。
実験用の.hファイルを作成し、その中にDq名前空間を定義します。これはミニライブラリの名前になります。
#pragma once namespace Dq { // : Container ..Args STL . template <template <typename...> typename Container, typename ...Args> // - class Enumerator { private: // Container<Args...> &List = Container<Args...>(); // typename Container<Args...>::iterator Position = List.begin() - 1; public: // bool MoveNext() { return ++Position != List.end(); } // typename Container<Args...>::value_type &operator*() const { return *Position; } // void Reset() { Position = List.begin() - 1; } // explicit Enumerator(const Container<Args...> &cont) : List(cont) {} }; }
すでに優れているので、1つの列挙子引数を目的の関数に渡すことができます。 C ++ 17では、このような列挙子の作成は非常に簡単です。
std::vector values { 0, 1, 2, 3, 4, 5 }; Dq::Enumerator i(values);
それまでの間、標準は最終的に承認されていません。補助機能を作成します。
namespace Dq { // template <template <typename...> typename Container, typename ...Args> Enumerator<Container, Args...> GetEnumerator(const Container<Args...> &cont) { return Enumerator<Container, Args...>(cont); } }
そして、次のように使用します。
std::vector<int> values{ 0, 1, 2, 3, 4, 5 }; auto i = Dq::GetEnumerator(values);
列挙子を使用して、最初の例を書き換える方法を次に示します。
void ActionTwo(auto &position) { if (!position.MoveNext()) { return; } // ... } void Action(auto &position) { if (!position.MoveNext()) { return; } // ... if (*position == ANY_VALUE) { ActionTwo(); } // ... }
私にとって、このソリューションは、STLイテレーターを使用するよりもはるかに美しく、日常の作業に便利です。 さて、小さな欠陥については、そのようなイテレーターは標準アルゴリズムには使用できません。 しかし、これは幸いにも簡単に修正でき、Enumeratorクラスにメソッドを追加するだけです。
template <template <typename...> typename Container, typename ...Args> typename Container<Args...>::iterator Enumerator<Container, Args...>::CurrentPostion() const { return Position; }
そして
template <template <typename...> typename Container, typename ...Args> typename Container<Args...>::iterator Enumerator<Container, Args...>::LastPostion() const { return List.end(); }
すべてのコードはGitHubの1つのファイルで見ることができます。
要約すると:
これで、STLアルゴリズムと互換性のある標準イテレータ用の便利なラッパー列挙子ができました。 人生はもう少し楽しくなり、世界のエントロピーは記事を書くコストのために増えました。私の自転車の客観的な有用性を判断すべきです。