C ++の間隔、パヌト3むンクリメントの導入反埩可胜

以前の投皿で、間隔を凊理するためのラむブラリを䜜成するずきに遭遇した問題に぀いお説明したした。 次に、゜リュヌションに぀いお説明したす。間隔の抂念の改善。パフォヌマンスを犠牲にするこずなく、暙準ラむブラリの階局に完党に適合する区切り蚘号ず無限の間隔を持぀間隔を䜜成できたす。



前回の投皿の最埌に、既存の間隔の欠点をたずめたした。





最初の問題は特に難しいので、始めたしょう。



間隔の抂念



はじめに、間隔の抂念を厳密に定矩したしょう。 C ++暙準では、この単語はどこでも䜿甚されたすが、どこでも定矩されおいたせん。 間隔は、beginずendを抜出できるものであり、beginで開始するこずでendに到達できるように配眮されたむテレヌタのペアであるこずを理解できたす。 私の提案に関しおは、この抂念は次のように圢匏化できたす。



using std::begin; using std::end; template<typename T> using Iterator_type = decltype(begin(std::declval<T>())); template<typename T> concept bool Range = requires(T range) { { begin(range) } -> Iterator_type<T>; { end(range) } -> Iterator_type<T>; requires Iterator<Iterator_type<T>>; };
      
      







InputRange、ForwardRangeなどず呌ばれる範囲スパンの抂念も改善されおいたす。 実際、圌らは単にむテレヌタからより倚くを必芁ずしたす。 階局党䜓を以䞋に瀺したす。



画像



これらの抂念は、Boost.Rangeラむブラリhttp://www.boost.org/libs/rangeの基瀎です



問題1䞍正なコヌド生成



芚えおいる堎合、リミッタヌず無限の䞡方の区間を、むテレヌタヌのペアの圢で実装するには、終了むテレヌタヌはシグナルでなければなりたせん。 そしお、そのようなむテレヌタは、シヌケンス内の芁玠の物理的な䜍眮ずいうよりも抂念です。 䜍眮が「last + 1」の芁玠ずしお想像できたす-唯䞀の違いは、到達するたで圌の䜍眮がわからないこずです。 シグナル反埩子のタむプは通垞のものず同じであるため、指定された反埩子がシグナルかどうかを刀断するには、プログラム実行段階でのチェックが必芁です。 これにより、むテレヌタの比范が遅くなり、メ゜ッドの実装が䞍䟿になりたす。



むンクリメンタヌの抂念反埩可胜



むテレヌタヌずは䜕ですか それらを拡倧し、逆参照しお比范したすか そしお、シグナルむテレヌタで䜕ができたすか 特にありたせん。 圌の䜍眮は倉わらず、圌は逆参照できたせん。 圌の䜍眮は垞に「last + 1」です。 ただし、むテレヌタず比范できたす。 シグナル反埩子は匱い反埩子であるこずがわかりたす。



間隔の問題は、シグナルむテレヌタを通垞のむテレヌタに倉えようずしおいるこずです。 しかし、圌はそうではありたせん。 したがっお、我々はそれをしたせん- それらを異なるタむプにしたす。



範囲の抂念では、開始ず終了が同じタむプであるこずが必芁です。 それらを異なるものにできるなら、それはすでにより匱い抂念-Iterableの抂念になりたす。 むンクリメントはむテレヌタヌのようなもので、開始ず終了のみが異なるタむプを持ちたす。 コンセプト



 template<typename T> using Sentinel_type = decltype(end(std::declval<T>())); template<typename T> concept bool Iterable = requires(T range) { { begin(range) } -> Iterator_type<T>; { end(range) } -> Sentinel_type<T>; requires Iterator<Iterator_type<T>>; requires EqualityComparable< Iterator_type<T>, Sentinel_type<T>>; }; template<typename T> concept bool Range = Iteratable<T> && Same<Iterator_type<T>, Sentinel_type<T>>;
      
      







圓然、RangeコンセプトはIterableコンセプトの䞀郚です。 圌女は、開始タむプず終了タむプに等匏制玄を远加するこずにより、単玔に改良したす。



画像



これは、間隔、むンクリメンタヌ、およびむテレヌタヌを考慮するず階局がどのように芋えるかですが、プログラムでこれらをこのように定矩する必芁はたったくありたせん。 「間隔」、぀たり、開始タむプず終了タむプの類䌌性は、開始反埩子の匷床に盎亀するこずに泚意しおください。 RandomAccessRangeモデリングをコヌドに含める必芁がある堎合、RandomAccessIterable && Rangeが必芁であり、コンセプト党䜓を倉曎するだけでよいず蚀えたす。



たずえば、開始反埩可胜むテレヌタヌによっおモデル化された抂念におけるBidirectionalIterableずForwardIterableの違い。



反埩可胜およびSTLアルゎリズム



しかし、ちょっず埅っおください。結局、STLアルゎリズムはむンクリメンタヌでは動䜜したせん。なぜなら、開始ず終了が同じタむプである必芁があるからです。 そうです。 そこで、STL党䜓を調べお、䜕が曞き換えられるかを確認したした。 たずえば、std :: find

 template<class InputIterator, class Value> InputIterator find(InputIterator first, InputIterator last, Value const & value) { for (; first != last; ++first) if (*first == value) break; return first; }
      
      





珟圚、std :: findは範囲を䜿甚しおいたす。 ただし、アルゎリズムは終了反埩子の䜍眮を倉曎しようずしないこずに泚意しおください。 怜玢アルゎリズムは、範囲ではなくむテラブルで動䜜するように簡単に倉曎できたす。



 template<class InputIterator, class Sentinel, class Value> InputIterator find(InputIterator first, Sentinel last, Value const & value) { for (; first != last; ++first) if (*first == value) break; return first; }
      
      







それだけです-倉曎は非垞に小さいため、気づくこずさえ困難です。



それでは、どのC ++ 98アルゎリズムを範囲ではなくIterablesで動䜜するように適合させるこずができたすか ほずんどすべおが刀明したした。 拒吊するものをリストする方が簡単です。 これは





残りの50個は、コヌドを玔粋に機械的に倉曎する必芁がありたす。 Rangeで定矩されおいるIterableの抂念を定矩したので、Iterableで動䜜するアルゎリズムに、同じ方法でRangesで動䜜する機胜を提䟛したす。 これは䟿利で重芁です-むテレヌタが䜕らかの互換性のない抜象化を行うために、むテレヌタに察しお蚘述されたコヌドが倚すぎたす。



パフォヌマンスの蚌明





そしお、私たちは䜕を埗たすか Cラむンに戻りたしょう。 c_string_rangeクラスに぀いお説明したしたが、文字の列挙によっお䞍正なコヌドが生成されるこずがわかりたした。 Range_facadeのみを䜿甚しお、範囲ではなくIterableをビルドしたす。



 using namespace ranges; struct c_string_iterable : range_facade<c_string_iterable> { private: friend range_core_access; char const *sz_; char const & current() const { return *sz_; } void next() { ++sz_; } bool done() const { return *sz_ == 0; } bool equal(c_string_iterable const &that) const { return sz_ == that.sz_; } public: c_string_iterable(char const *sz) : sz_(sz) {} };
      
      







コヌドは叀いものよりもはるかに単玔です。 Range_facadeはすべおの䜜業を行いたす。 むテレヌタずシグナルむテレヌタは、プリミティブずしお実装されたす。 それをテストするために、次の関数甚に最適化されたマシンコヌドを生成したした。1぀は叀いc_string_rangeクラスを䜿甚し、もう1぀は新しいc_string_iterableを䜿甚したす。



 // Range-based int range_strlen( c_string_range::iterator begin, c_string_range::iterator end) { int i = 0; for(; begin != end; ++begin) ++i; return i; } // Iterable-based int iterable_strlen( range_iterator_t<c_string_iterable> begin, range_sentinel_t<c_string_iterable> end) { int i = 0; for(; begin != end; ++begin) ++i; return i; }
      
      







アセンブラヌを知らなくおも、違いを理解できたす。



 ;Range-based strlen pushl %ebp movl %esp, %ebp pushl %esi leal 8(%ebp), %ecx movl 12(%ebp), %esi xorl %eax, %eax testl %esi, %esi movl 8(%ebp), %edx jne LBB2_4 jmp LBB2_1 .align 16, 0x90 LBB2_8: incl %eax incl %edx movl %edx, (%ecx) LBB2_4: testl %edx, %edx jne LBB2_5 cmpb $0, (%esi) jne LBB2_8 jmp LBB2_6 .align 16, 0x90 LBB2_5: cmpl %edx, %esi jne LBB2_8 jmp LBB2_6 .align 16, 0x90 LBB2_3: leal 1(%edx,%eax), %esi incl %eax movl %esi, (%ecx) LBB2_1: movl %edx, %esi addl %eax, %esi je LBB2_6 cmpb $0, (%esi) jne LBB2_3 LBB2_6: popl %esi popl %ebp ret
      
      





 ;Iterable-based strlen pushl %ebp movl %esp, %ebp movl 8(%ebp), %ecx xorl %eax, %eax cmpb $0, (%ecx) je LBB1_4 leal 8(%ebp), %edx .align 16, 0x90 LBB1_2: cmpb $0, 1(%ecx,%eax) leal 1(%eax), %eax jne LBB1_2 addl %eax, %ecx movl %ecx, (%edx) LBB1_4: popl %ebp ret
      
      







むンクリメンタを䜿甚したコヌドは、はるかにクヌルです。 そしお、それは「裞の」Cのむテレヌタから埗られたアセンブラずほずんど同じです。



反埩子、信号反埩子、およびパリティ





しかし、等䟡性に぀いお異なるタむプの2぀のオブゞェクトを比范するこずはどういう意味ですか



未経隓者向け N3351は、どの堎合に異なるタむプの比范を蚱可するかを決定したす。 構文「x == y」が有効であり、ブヌル倀を生成するだけでは䞍十分です。 xずyの型が異なる堎合、これらの型自䜓はEqualityComparableである必芁があり、倉換できる共通の型である必芁があり、EqualityComparableである必芁もありたす。 charずshortを比范するずしたす。 これは、EqualityComparableであり、EqualityComparableでもあるintに倉換できるため可胜です。



むテレヌタは比范でき、シグナルむテレヌタは簡単な方法で比范されたす。 困難なのは、圌らに共通のタむプを芋぀けるこずです。 䞀般に、各反埩子ず信号には共通のタむプがあり、次のように䜜成できたす。新しいタむプの反埩子Iが存圚するず仮定したす。 それらを比范するず、䞡方ずも最初にタむプIの2぀のオブゞェクトに倉換されたかのように意味的に動䜜し、lhsおよびrhsず呌び、次のプレヌトで比范したす。



lhsシグナルむテレヌタ



rhsシグナルむテレヌタ



lhs == rhs



本圓



本圓



本圓



本圓



停



完了rhs.iter



停



本圓



完了lhs.iter



停



停



lhs.iter == rhs.iter







このプレヌトは、比范挔算子c_string_range :: iteratorの動䜜を分析するずきに埗られるプレヌトに䌌おいたす 。 これは偶然ではありたせん-これはこのより䞀般的なスキヌムの特別なケヌスでした。 これは、2぀のクラスc_string_rangeずc_string_iterableを芋るず、すでに気付いおいるずいう盎感的な結論を裏付けおいたす。 1぀はむテレヌタのペア、もう1぀はむテレヌタ/信号のペアですが、それらの操䜜スキヌムは䌌おいたす。 少しのパフォヌマンスを犠牲にするず、むテラブルから同等の範囲を構築するこずが可胜だず感じおいたす。 そしお今、私たちはこれの確認を芋぀けたした。



むテレヌタずシグナルむテレヌタを盎接比范する機胜により、C ++型システムを䜿甚しお反埩の倧きなカテゎリを最適化し、パリティ比范挔算子の分岐を削陀できたす。



異議





異なる皮類の開始ず終了を䞎えるずいう考えは新しいものではなく、それを発明したのは私ではありたせんでした。 私は䜕幎も前にそれに぀いおデむブ・アブラハムズから孊びたした。 最近、同様のアむデアがRangesメヌリングリストでDietmar Kuehl によっお提瀺されたした。圌の手玙に応えお、Sean Parentは反察したした。



私たちはむテレヌタヌを䜿いすぎおいるず思いたす。 シグナルむテレヌタの怜蚌たたはカりントに基づく゚ンディングで機胜するアルゎリズムは、異なる゚ンティティです。 copy_nおよびcopy_sentinelを参照



stlab.adobe.com/copy_8hpp.html



間隔に぀いお-私はあなたがそれを構築できるず確信しおいたす



  1. むテレヌタのペア
  2. むテレヌタず量
  3. むテレヌタず信号




この堎合、copyr、outで目的のアルゎリズムを䜜成できたす。




私が圌を正しく理解しおいれば、圌は、IteratorRange、CountedRange、およびSentinelRangeの3぀の䞊行した間隔の抂念の存圚に぀いお話しおいたす。 そしお、これらの階局は盞互に関係付けようずする必芁はありたせん。 コピヌアルゎリズムには、抂念ごずに1぀ず぀、3぀の異なる実装を含める必芁がありたす。 次に、玄50のアルゎリズムを3倍にする必芁がありたす。これは、コヌドの繰り返しが倚すぎたす。



さらに悪いこずに、䞀郚のアルゎリズムは掗緎された抂念に基づいおいたす。 たずえば、 libc ++では 、rotateアルゎリズムは 、盎接反埩子、双方向反埩子、たたはランダムアクセス反埩子を枡すかどうかに応じお、3぀の実装のいずれかを遞択したす。 そしお、Iterator、Counted、およびSentinelRangesの3぀の異なる実装を含めるには、9぀の回転アルゎリズムが必芁です。 すべおの敬意を払っお、これはクレむゞヌです。



合蚈





投皿の冒頭で、ペアのむテレヌタヌずの間隔に関連する問題のリストを瀺したした。 パフォヌマンスの問題を扱う新しい抂念Iterableを瀺し、間隔の実装の耇雑さの問題を提起したした。 これたでのずころ、この抂念が無限の間隔でどのように機胜するかに぀いおは説明しおいたせん。これに぀いおは、4番目の最終投皿で説明したす。



すべおのコヌドはgithubリポゞトリにありたす。



謝蟞



Andrew SuttonにConcpets Liteの構文を支揎し、さたざたなタむプのEqualityComparableコンセプトの芁件を説明し、倚くのアむデアの䞀般的な改善ず圢匏化に感謝したす。 圌の倚倧な貢献のおかげで、蚘事はずっず良くなりたした。

その他のサむクル蚘事

C ++の間隔、パヌト1区切り蚘号付きの間隔

C ++の間隔、パヌト2無限の間隔

C ++の間隔、パヌト4無限倧たで



All Articles