vector <bool>がSTLコンテナではないのはなぜですか?


99

スコットマイヤーズの本の効果的なSTLの項目18 :標準テンプレートライブラリの使用を改善するための50の特定の方法vector <bool>は、STLコンテナではなく、実際にはboolsを保持しないため、避けるようにと言っています。

次のコード:

vector <bool> v; 
bool *pb =&v[0];

コンパイルされず、STLコンテナの要件に違反します。

エラー:

cannot convert 'std::vector<bool>::reference* {aka std::_Bit_reference*}' to 'bool*' in initialization

vector<T>::operator []戻り値の型はであるはずですがT&、なぜそれがvector<bool>ですか?

なに vector<bool>実際には構成されていますか?

アイテムはさらに言う:

deque<bool> v; // is a STL container and it really contains bools

これをの代替として使用できますvector<bool>か?

誰もがこれを説明できますか?


22
これはC ++ 98の設計ミスでしたが、現在は互換性のために保持されています。
オクタリスト2013

8
@ g-makulik、それを使用してもコンパイルされないわけではありません。要素へのポインタに要素のアドレスを格納できないのは、要素にbool独自のアドレスがないためです。
クリス2013

2
おそらくこれが役立つでしょう:stackoverflow.com/questions/670308/alternative-to-vectorbool
chris

1
@ g-makulik std::vector<bool> v;がコンパイルされます。&v[0]しない(一時的なアドレスを取る)。
オクタリスト2013

4
vector<bool>悪い担当者がいますが、完全に正当なわけではありません。isocpp.org
TemplateRex

回答:


114

スペース最適化の理由から、C ++標準(C ++ 98まで)はvector<bool>、各ブールが通常のブールのように1バイトではなく1バイトのスペースのみを使用する特別な標準コンテナーとして明示的に呼び出されます(一種の「動的ビットセット」)。この最適化と引き換えに、通常の標準コンテナーのすべての機能とインターフェースを提供するわけではありません。

この場合、バイト内のビットのアドレスを取得できないため、をoperator[]返すことはできませんbool&が、代わりに問題の特定のビットを操作できるプロキシオブジェクトを返します。このプロキシオブジェクトはでないためbool&bool*「通常の」コンテナでのこのような演算子の呼び出しの結果と同じようにアドレスを割り当てることはできません。つまり、これはbool *pb =&v[0];有効なコードではありません。

一方、そのdequeような特殊化は呼び出されないため、各ブール値は1バイトを取得し、からの戻り値のアドレスを取得できますoperator[]

最後に、MS標準ライブラリの実装は、(おそらく)dequeに小さなチャンクサイズを使用するという点で最適ではないことに注意してください。つまり、dequeを代用として使用することが常に正しい答えとは限りません。


5
他のSTLコンテナーが特殊化されている、または明示的に呼び出されている他のデータ型がありますか?
P0W 2013

3
これはC ++ 11 std :: array <bool>に適用されますか?
Sergio Basurco、2015

4
@chuckleplantいいえ、std::array単なる配列の周りのテンプレート化されたラッパーであり、のT[n]ようないくつかのヘルパー関数size()、コピー/移動のセマンティクス、およびイテレーターがSTL互換にするために追加されています-(ありがたいことに)独自の原則に違反していません(これらの懐疑論:)「」の「専門bool」。
underscore_d 2015

単なる選択-sizeof(bool)は必ずしもバイトではありません。stackoverflow.com/questions/4897844/...
ウリラズ

30

vector<bool>値に1ビットのみを使用した圧縮形式のブール値が含まれます(bool []配列のように8ではありません)。C ++ではビットへの参照を返すことができないため、メモリ内のビットへのインターフェイスを提供し、標準の演算子とキャストを使用できるようにする、特別なヘルパー型である「ビット参照」があります。


1
@PrashantSrivastava deque<bool>は特殊化されていないため、文字通り単なるブール値を保持する両端キューです。
Konrad Rudolph

@PrashantSrivastava vector<bool>には特定のテンプレート実装があります。たぶん、などの他のSTLコンテナはdeque<bool>そうではないので、他のタイプと同様にブールを保持します。
Ivan Smirnov

rustで同様のことを尋ねる質問は次のとおりです。シングルビットのブール値は使用できません。stackoverflow.com
andy boot

25

問題は、真の参照ではなくプロキシ参照オブジェクトvector<bool>返すため、C ++ 98スタイルのコードがコンパイルされないことです。ただし、プロキシポインターオブジェクト返す場合は、最新のC ++ 11を使用してコンパイルできます。Howard Hinnantはこのようなプロキシ参照とポインタを使用する際のアルゴリズムの改善について詳しく説明したブログ投稿を書いています。bool * p = &v[0];auto p = &v[0];operator&

Scott Meyersは、プロキシクラスに関するより効果的なC ++の長い項目30を持っています。組み込みの型をほとんど模倣する長い道のりをたどることができます。特定の型Tについて、プロキシのペア(例:reference_proxy<T>iterator_proxy<T>)は、reference_proxy<T>::operator&()iterator_proxy<T>::operator*()の逆であるという意味で相互に一貫性を持たせることができます。

ただし、ある時点で、プロキシオブジェクトをマッピングして、T*またはのように動作させる必要がありますT&。イテレータプロキシの場合、すべての機能を再実装することなくoperator->()、テンプレートTのインターフェースにオーバーロードしてアクセスできます。ただし、参照プロキシの場合、をオーバーロードする必要があります。これはoperator.()現在のC ++では許可されていません(Sebastian Redl がBoostCon 2013でそのような提案提示しましたが)。.get()参照プロキシ内のメンバーのように詳細な回避策を作成するか、すべてを実装できますが、これにより、組み込み構文が失われるか、型変換の組み込みセマンティクスを持たないユーザー定義変換が導入されます(引数ごとにほとんどのユーザー定義の変換)。Tのインターフェースをことができます(これは、vector<bool>::bit_reference

TL; DRvector<bool>標準は実際の参照を必要とするため、コンテナではありませんが、少なくともC ++ 11(auto)ではC ++ 98よりもはるかに近いコンテナのように動作させることができます。


10

多くの人は、vector<bool>専門化を誤りであると考えています。

論文「C ++ 17でのVestigialライブラリパーツの非推奨」では、ベクトルの部分的な特殊化
再考する提案があり ます。

std :: vectorのbool部分的な特殊化は、コンテナの要件を満たしていません。特に、そのイテレータはランダムアクセスイテレータの要件を満たしていません。このコンテナを廃止する以前の試みは、C ++ 11、N2204で拒否されました


拒否の理由の1つは、テンプレートの特定の特殊化を廃止することの意味が明確でないことです。これは注意深い表現で対処できます。より大きな問題は、ベクトルの(パックされた)特殊化が、標準ライブラリのクライアントが真に求めている重要な最適化を提供しているが、もはや利用できないことです。N2050などの代替施設が提案されて承認されるまで、標準のこの部分を廃止することはできません。残念ながら、現在、ライブラリ進化ワーキンググループに提供されているそのような改訂された提案はありません。


5

実装方法を見てください。STLはテンプレートに大きく基づいて構築されているため、ヘッダーにはテンプレートが実行するコードが含まれています。

たとえば、こちらstdc ++実装を見てください

stl準拠のビットベクトルではありませんが、ここからのllvm :: BitVectorも興味深いです

の本質llvm::BitVectorは、ネストされたクラスと呼ばれ、いくつかの制限付きで動作を同様にreferenceするための適切な演算子のオーバーロードです。以下のコードは、BitVectorが呼び出されたクラスをどのように隠して実際の実装をブール値の実際の配列のように動作させ、各値に1バイトを使用しないかを示す簡略化されたインターフェイスです。BitVectorvectorreference

class BitVector {
public:
  class reference {
    reference &operator=(reference t);
    reference& operator=(bool t);
    operator bool() const;
  };
  reference operator[](unsigned Idx);
  bool operator[](unsigned Idx) const;      
};

ここのこのコードには素晴らしいプロパティがあります:

BitVector b(10, false); // size 10, default false
BitVector::reference &x = b[5]; // that's what really happens
bool y = b[5]; // implicitly converted to bool 
assert(b[5] == false); // converted to bool
assert(b[6] == b[7]); // bool operator==(const reference &, const reference &);
b[5] = true; // assignment on reference
assert(b[5] == true); // and actually it does work.

このコードには実際に欠陥があります。実行してみてください:

std::for_each(&b[5], &b[6], some_func); // address of reference not an iterator

assert( (&b[5] - &b[3]) == (5 - 3) );失敗するため(内でllvm::BitVector)、機能しません

これは非常にシンプルなllvmバージョンです。std::vector<bool>その中にも動作するイテレータがあります。したがって、呼び出しfor(auto i = b.begin(), e = b.end(); i != e; ++i)は機能します。そしてまたstd::vector<bool>::const_iterator

ただし場合によってはstd::vector<bool>動作が異なるという制限があります。


3

これはhttp://www.cplusplus.com/reference/vector/vector-bool/から取得さます

boolのVectorこれは、bool型の要素に使用され、スペースを最適化するvectorの特殊なバージョンです。

これは、次のように変更された、特殊化されていないバージョンのベクターのように動作します。

  • ストレージは必ずしもブール値の配列である必要はありませんが、ライブラリの実装はストレージを最適化して、各値が
    1ビットで格納されるようにする場合があります。
  • 要素はアロケーターオブジェクトを使用して構築されませんが、その値は内部ストレージの適切なビットに直接設定されます。
  • メンバー関数の反転とメンバー交換用の新しい署名。

  • ブールの参照をエミュレートするインターフェースを使用して、コンテナーの内部ストレージの個々のビットにアクセスする特別なメンバー型の参照であるクラス。逆に、メンバータイプconst_referenceはプレーンなブール値です。
  • コンテナーが使用するポインターおよびイテレーターのタイプは、ポインターや適合イテレーターである必要はありません
    が、期待される動作のほとんどをシミュレートする必要があります。

これらの変更は、この特殊化への風変わりなインターフェースを提供し、処理よりもメモリーの最適化を優先します(これはユーザーのニーズに合っている場合とない場合があります)。いずれの場合も、boolのベクトルの特殊化されていないテンプレートを直接インスタンス化することはできません。この範囲が別のタイプ(char、unsigned char)またはコンテナー(dequeなど)を使用してラッパータイプを使用したり、特定のアロケータータイプにさらに特化したりしないようにするための回避策。

bitsetは、固定サイズのビット配列に同様の機能を提供するクラスです。


1
これは質問に直接答えるものではありません。せいぜい、この一般的な概要で説明されているものが非STLになる理由を読者が推測する必要があります。
underscore_d 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.