9

私自身、私が取り組んでいるプロジェクトでは、含まれる値が決して負になることはありませんが、ほとんどの場合、符号付き整数が最良の選択であると確信しています。(特に 0 から 20 の間の値しか保持できない整数の場合、ループの単純な逆、バグの可能性が少なくなります。)

これがうまくいかない場所の大部分は、std::vector の単純な繰り返しです。多くの場合、これは過去には配列でしたが、後で std::vector に変更されました。したがって、これらのループは一般的に次のようになります。

for (int i = 0; i < someVector.size(); ++i) { /* do stuff */ }

このパターンは非常に頻繁に使用されるため、符号付き型と符号なし型のこの比較に関するコンパイラ警告スパムの量によって、より有用な警告が隠される傾向があります。INT_MAX 要素を超えるベクトルは絶対にないことに注意してください。これまで、コンパイラの警告を修正するために 2 つの方法を使用していたことに注意してください。

for (unsigned i = 0; i < someVector.size(); ++i) { /*do stuff*/ }

これは通常は機能しますが、ループに「if (i-1 >= 0) ...」などのコードが含まれていると、黙って中断する可能性があります。

for (int i = 0; i < static_cast<int>(someVector.size()); ++i) { /*do stuff*/ }

この変更には副作用はありませんが、ループが読みにくくなります。(そして、それはより多くのタイピングです。)

そこで、次のアイデアを思いつきました。

template <typename T> struct vector : public std::vector<T>
{
    typedef std::vector<T> base;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }
    int capacity() const { return base::capacity(); }

    vector()                  : base() {}
    vector(int n)             : base(n) {}
    vector(int n, const T& t) : base(n, t) {}
    vector(const base& other) : base(other) {}
};

template <typename Key, typename Data> struct map : public std::map<Key, Data>
{
    typedef std::map<Key, Data> base;
    typedef typename base::key_compare key_compare;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }

    int erase(const Key& k) { return base::erase(k); }
    int count(const Key& k) { return base::count(k); }

    map() : base() {}
    map(const key_compare& comp) : base(comp) {}
    template <class InputIterator> map(InputIterator f, InputIterator l) : base(f, l) {}
    template <class InputIterator> map(InputIterator f, InputIterator l, const key_compare& comp) : base(f, l, comp) {}
    map(const base& other) : base(other) {}
};

// TODO: similar code for other container types

表示されるのは、基本的に、size_type をオーバーライドして「int」のみを返すメソッドを持つ STL クラスです。これらは継承されないため、コンストラクターが必要です。

既存のコードベースでこのようなソリューションが見られるとしたら、開発者としてこれをどう思いますか?

「うわあ、彼らは STL を再定義している、なんと巨大な WTF だ!」と思いますか?それとも、これはバグを防ぎ、読みやすさを向上させるための素晴らしいシンプルなソリューションだと思いますか? それとも、これらすべてのループを std::vector<>::iterator を使用するように変更するのに (半日) ほど費やしたことを知りたいですか?

(特に、この解決策が生データ (例: unsigned char) とビット マスク以外での unsigned 型の使用の禁止と組み合わされた場合。)

4

7 に答える 7

7

STL コンテナーから公に派生させないでください。それらには、誰かがベースへのポインターを介してオブジェクトの1つを削除した場合に未定義の動作を呼び出す非仮想デストラクタがあります。たとえばベクトルから派生する必要がある場合は、非公開で行い、公開する必要がある部分をusing宣言で公開します。

ここではsize_t、ループ変数として a を使用します。シンプルで読みやすいです。intインデックスを使用すると n00b として公開されるとコメントした投稿者は正しいです。ただし、反復子を使用してベクトルをループすると、少し経験豊富な n00b (ベクトルの添字演算子が一定時間であることを認識していない) として公開されます。(vector<T>::size_type正確ですが、不必要に冗長なIMOです)。

于 2008-11-09T12:58:33.427 に答える
4

「イテレータを使用しないと、n00bに見える」というのは問題の良い解決策ではないと思いますが、std::vectorから派生することはそれよりもはるかに悪いように見えます。

まず、開発者は、vectorがstd:.vectorであり、mapがstd::mapであることを期待しています。第2に、ソリューションは、他のコンテナー、またはコンテナーと対話する他のクラス/ライブラリーに対応していません。

はい、イテレータは醜く、イテレータループはあまり読みやすくなく、typedefは混乱をカバーするだけです。しかし、少なくとも、それらは拡張性があり、標準的なソリューションです。

私の解決策は?stl-for-eachマクロ。それは問題がないわけではありませんが(主に、それはマクロです、うんざりです)、それは意味を理解します。これは、たとえばこれほど高度ではありませんが、機能します。

于 2008-11-09T14:19:05.090 に答える
3

間違いなくイテレータを使用してください。すぐに、次のように読みやすくするために、「auto」タイプを使用できるようになります(懸念事項の1つ)。

for (auto i = someVector.begin();
     i != someVector.end();
     ++i)
于 2008-11-09T14:12:44.323 に答える
3

このコミュニティ wiki を作成しました。編集してください。「int」に対するアドバイスにはもう同意しません。今では悪くないと思います。

はい、私はリチャードに同意します。'int'それらのようなループでカウント変数として使用しないでください。以下は、インデックスを使用してさまざまなループを実行する方法です (実行する理由はほとんどありませんが、これが役立つ場合もあります)。

前方

for(std::vector<int>::size_type i = 0; i < someVector.size(); i++) {
    /* ... */
}

後方

これを行うことができます。これは完全に定義された動作です。

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* ... */
}

まもなく、c++1x (次の C++ バージョン) がうまく機能するようになるので、次のようにすることができます。

for(auto i = someVector.size() - 1; i != (decltype(i)) -1; i--) {
    /* ... */
}

0 未満にデクリメントすると、i は符号なしであるため、ラップ アラウンドします。

しかし、署名されていないとバグが丸呑みされます

これは、間違った方法 (を使用) にするための引数であってはなりません'int'

上記の std::size_t を使用しないのはなぜですか?

C++ 標準では23.1 p5 Container Requirements、が someであるT::size_typeため、この型は実装定義の符号なし整数型であると定義されています。上記の forを使用すると、バグが静かに侵入します。が よりも小さいか大きい場合は、オーバーフローするか、 ifに達しません。同様に、ループの状態は完全に壊れていたでしょう。TContainerstd::size_tiT::size_typestd::size_ti(std::size_t)-1someVector.size() == 0

于 2008-11-09T12:33:34.850 に答える
0

vector.size()size_tvar を返すので、に変更intするだけsize_tで問題ありません。

リチャードの答えはより正確ですが、単純なループでは大変な作業です。

于 2008-11-09T13:14:10.650 に答える
0

あなたは問題を考えすぎています。

size_t 変数を使用することをお勧めしますが、プログラマーが unsigned を正しく使用することを信頼できない場合は、キャストを使用して醜さを処理してください。インターンにそれらをすべて変更してもらい、その後は心配する必要はありません。エラーとして警告を有効にすると、新しい警告が入り込むことはありません。現在、ループは「醜い」ものになっているかもしれませんが、これは、署名付きと未署名に対する宗教的スタンスの結果として理解できます。

于 2008-11-09T13:14:17.497 に答える