セットの質問に

アリス:なぜこの場所はとても奇妙な場所なのですか?

ドードー:しかし、他のすべての場所はそれほど奇妙ではないからです。 少なくとも1つの非常に奇妙な場所が必要です。









それでは、MKの要件に合わせてテンプレートクラスBitSetのテキストを見てみましょう。最適化の主要な領域は以前に定義されています。 もちろん、独自のクラスをゼロから作成することもできますが、STLライブラリ(splと混同しないでください)は古くから知られており、あらゆる場所で使用されているため、優れたソリューションに精通する機会を無視しないでください。 まず、ソースコードを見つける必要があります。インターネットを少し旅行した後、MinGWでディレクトリを開いて、必要なファイルを探しました。これについては、さらに詳しく説明します。



最初は、著作権とライセンスのテキスト、およびさまざまな警告が記載されたページ以上のものがあります。これはスキップできます。これは弁護士向けです。 次に、インクルードを作成し、著者に感謝します。各ファイルには、各ファイルから何を取得したいかについてのコメントが付いています。誰もがそうするでしょう。 定義は次に来る、私はコードのクリーンさの狂信者ではなく、私は議論しません-ストレージユニットのビット数の定義を見てみましょう、これは符号なしの長いです。 Definesの滑りやすいパスに沿って続けて、uint8_fastに等しい独自のストレージユニット定義を探していますが、これでは明らかに十分ではないことを理解しています。 ちなみに、私はすぐにスタイルの作者に感謝します-モジュールの最後で、彼らがクリーンアップし、内部定義を破壊します-私はもはやこれが現代の生産で見られることを望みませんでした。



そして、ここで軽い誤解が始まります-最初に、テンプレート補助構造体struct _Base_bitset(クラスは交換可能であるため、正確に構造ではなく)を決定し、そこでデータストレージのタイプを決定し、再び直接指定されます-それを独自のものに変更します。 次に、この補助構造の2つのインスタンスが決定されます-1つのストレージユニット(これはコードの効率を高めるために理解できる)と、このコードに触れるまで、ストレージをまったく使用しない(おそらくエラー処理のための)退化した例です。



次に、テンプレート構造から派生したメインクラスクラスビットセット(現在のクラス)を見つけます(はい、Cでは可能ですが、これを行う理由は明らかではありませんが、可能です)、ストレージタイプが再び決定され、再び私たちのものに変更されます。 しかし、ここでは、なぜ型が親構造から継承されず、驚いたことに驚いたのですか?(驚いたことに、テンプレートクラス-これは私が日常生活で行っていることではありません)、テンプレートクラスの継承は通常のものとは多少異なります。 つまり、継承はまったく同じであり、親のすべてのエンティティは娘クラス(誰も私をジェンダーショーヴィニストに連れて行かないことを望みます)クラスで利用できますが、フルネームで示す必要があります。 さて、これはまだ理解できます。さもないと、コンパイラはどのインスタンス化オプションを適用するかを推測しませんが、クラスで定義された型を使用する場合、typenameキーワードを追加する必要があります。 これは明らかな解決策ではありません。コンパイラの診断に特に満足しました。つまり、「親クラスの型を持っている可能性があります」という意味です。はい、ありがとう、キャプテン、それが私が意図したことです。私にとっては簡単なことではありません。 ただし、このような診断は古いバージョンのGCCで行われ、現在のメッセージでは「typenameキーワードを忘れた可能性があります」ということがより明確になります。 テンプレートクラスのコンパイラの診断メッセージを理解することは、一般的に別の歌のトピックであり、この歌は決して楽しいものではありません。



そのため、子クラスでコンストラクトを常に使用したくない場合



std::_Base_bitset<Nb>::_WordT
      
      





(そして、私たちは望んでいません、否定しますか?)それから、3つの方法があります



1)些細な



 #define _WordT typename _Base_bitset<_Nb>::_WordT
      
      





2)明らか-グローバルレベルでの型の決定、私はそれがより好きだった



3)奇妙なファンのために



 typedef typename _Base_bitset<_Nb>::_WordT _WordT;
      
      





(壮大なクロム松葉杖の永続的な感覚)。 個人的には、実装は継承チェーンを考慮すべきではないので、3つすべてのケースで親クラスの直接的な指示に満足していませんが、多分私は何かを理解していません。



方法もあります4)



  using _WordT = std::_Base_bitset<Nb>::_WordT;
      
      





しかし、私のバージョンのコンパイラーでは動作しませんので、動作しません。



マージンに関する注意(PNP)-すばらしい本「C ++ for real Programmers」(当時、誤ってダウンロードし、リアルタイムシステムでC ++に専念していると判断した)には、「優雅さは決してないシンプルさ(C ++という言葉とシンプルさは見かけ上の矛盾で耳を引きます)が、その潜在的な能力に。 すべてのい問題の背後には、ある種の巧妙なイディオム、エレガントな言語のフェイントが隠されています。そのおかげで、問題は目の前で溶けてしまいます。」本は本当に素晴らしいので、上記の引用が正しいことを意味します(ここには特定の論理的な広がりがあることを理解しています)残念ながら、問題はありますが、スマートなイディオムでストレスを感じています。



わずかな戸惑いの残りの感覚にもかかわらず、問題は解決され、結果を楽しむことができます-しかし、それはありませんでした。 テストセットのサイズを15から5に変更すると、予期しないコンパイルエラーが完全に発生しました。 いいえ、すべてが明確で、テンプレートパラメータ1を使用した未変更のインスタンス化は機能しましたが、外からは非常に奇妙に見えます-定数を変更すると、プログラムはコンパイルを停止します。



もちろん、この実装と別の実装を変更することもできますが、そのようなアプローチはDRY原則に大きく違反しているように見えます。 可能な解決策があり、それらのうちの1つ以上があります1)明白な-再び定義し、2)些細な-再びグローバルレベルでの型定義ですが、この場合はモジュールの外に出ますが、検討中の実装では基本構造から突き出ています、修飾子も記述する必要はありません。



したがって、私はオプション3)型とそれからのすべてのインスタンスの継承を決定するための基本クラスに傾倒しています。 さらに、親クラスのエンティティも保護されているため、突出しません。 次に、面白いプロパティを見つけます。おそらく、柔軟性のためにC ++を賞賛する必要があります-ストレージを持たないバリアントの場合、型は不要です ライブラリのもう1つの欠点をすぐに発見しました。3つすべてのケースで、ストレージユニットの数を計算する操作が毎回設定されます



 static size_t _S_whichword
      
      





そしてその中のビットマスクは_S_maskbitであり、完全に同一です-また、それらを基本クラスに移動します。 この場合、「デッド」コードが検出されます-_S_whichbyteメソッド、何をすべきかさえわかりません-一方で、良いトーンルールはその削除を必要とします-一方で、テンプレートの場合、これは結果のコードに影響しません。 「何かを理解しないでください-触れないでください」というルールを使用して、この方法を終了します。



原則として、ストレージの種類に関する変更が完了し、テストを開始できます。 そして、すぐに実装の欠如に気付きました-MSP430アーキテクチャでは、何らかの理由で、バイトではなく2バイトのワードが割り当てられます。 もちろん、以前のようにダブルワードではありませんが、それでも最小限の(あらゆる意味で)コードを求めて戦っています。 コンパイラーは、このアーキテクチャーでは、タイプuint_fast8_tのサイズが2であることをコンパイラーが確信していることがわかります。ただし、コマンドシステムにはバイトによる操作があり、コンパイラー自体はそれらを完全に使用します。 この型を使用するという考え方は妥協されており、データ型uint8_tを直接設定する必要があります。 さて、バイトの処理が失敗し、uint_fast8_t型が1(コンパイラーで使用可能なものはどれも)でないタイプのアーキテクチャーがある場合、他のすべての速度を犠牲にする必要があります。



修正されたバージョンをテストすると、さまざまなアーキテクチャおよびさまざまなパラメーターで正しい動作が示されますが、開発されたシフトなしでMKのビットマスクを計算することには疑問が残ります。この場合はMSP430およびAVRです。 原則として、MKアーキテクチャに関係なく、配列からビットマスクを読み取る方法をすべての場合のメソッドにすることができます。 ソリューションは非常に効果的です。インデックス化されたすべての開発アーキテクチャでは問題ありませんが、クイックシフトと比較して時間のロスがあります。「クラスが速くなると、彼は言った、最適化する」という言葉で指を突いたくない彼は言った。



したがって、uint_fast16_t型のサイズが他のすべてのアーキテクチャと異なる2つの脆弱なアーキテクチャの異なる実装が必要です。64ビットバージョンでは2対4、または8です。 条件付きコンパイルは役に立ちませんが、最適化を期待して条件を設定することはサムライの方法ではなく、パターンが残っています。 クラスのテンプレートメソッドを作成しようとしていますが、部分的な特殊化は使用できません。そうすべきであることがわかり、なぜこれが良い解決策であるかを説明する記事を見つけます。 正直なところ、私はこの決定が良い理由を理解していませんでした。おそらくこれは宗教的な配慮によるものです。 それにもかかわらず、私たちは尋ねられず、何をするべきか-あなたはフレンドリーなテンプレート関数を作成することができます(それは不可能であり、同じ理由で部分的な専門化は禁止されています)、もう1つの解決策があります、それは面白いようです-クラスの本体の外側のメソッドの部分的な専門化。 別のテンプレート関数を作成して、クラスメソッドからアクセスすることもできます。どちらのメソッドが正しいかわかりません。これを選択しました。 はい、彼女はクラスの内部エンティティにアクセスできませんが、この特定のケースでは必要ではありません。必要に応じて何をすべきか、「問題が発生したときに解決します」。



これで必要なものはすべて揃ったので、テスト済みのフラグメントを収集し、初期最適化問題を解決するコードを取得します。 同時に、コードをよりコンパクトにし(したがって、後者は議論の余地がありますが、より理解しやすくなりました)、多数の繰り返しを削除し、突き出ている冗長なエンティティを削除しました。 修正されていない唯一のものは、構造とクラスの混合ですが、ここではそのような実装の理由がわかりません。したがって、念のため、この部分には触れません。



2番目の投稿では、セットの2番目のバージョンの実装に専念します。それなしでは、多くのことが起こりました。



All Articles