libc ++のvector <bool> :: const_referenceがboolではないのはなぜですか?


92

セクション23.3.7クラスvector<bool>[vector.bool]、パラグラフ1は次のように述べています。

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

ただし、libc ++を使用すると、このプログラムはコンパイルに失敗します。

#include <vector>
#include <type_traits>

int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

さらに、C ++標準はこの仕様でC ++ 98までずっと一貫していることに注意します。また、libc ++が最初に導入されて以来、libc ++はこの仕様に一貫して準拠していないことにも注意してください。

この不適合の動機は何ですか?

回答:


99

この拡張の動機は、適合プログラムによって検出可能であり、したがって適合していないため、参照(constおよびその他)に関して、vector<bool>より振る舞うようvector<char>にすることです。

前書き

1998年以来、vector<bool>「あまりコンテナではない」と非難されてきました。 LWGの最初の問題の1つであるLWG 96が議論を始めました。17年後の今日、vector<bool>ほとんど変わっていません。

このホワイトペーパーでは、の動作がのvector<bool>他のすべてのインスタンス化とどのように異なり、vector汎用コードに悪影響を与えるかについて、いくつかの具体的な例を取り上げます。ただし、同じペーパーでvector<bool>は、適切に実装された場合に非常に優れたパフォーマンスプロパティが持つ可能性があることについて詳細に説明しています。

概要vector<bool>悪いコンテナではありません。実際にはかなり便利です。それは単に悪い名前を持っています。

戻る const_reference

上記で紹介し、ここ詳しく説明しvector<bool>ているように、一般的なコードでは、他のvectorインスタンス化とは動​​作が異なるのが悪い点です。具体的な例を次に示します。

#include <cassert>
#include <vector>

template <class T>
void
test(std::vector<T>& v)
{
    using const_ref = typename std::vector<T>::const_reference;
    const std::vector<T>& cv = v;
    const_ref cr = cv[0];
    assert(cr == cv[0]);
    v[0] = 1;
    assert(true == cv[0]);
    assert(cr == cv[0]);  // Fires!
}

int
main()
{
    std::vector<char> vc(1);
    test(vc);
    std::vector<bool> vb(1);
    test(vb);
}

標準仕様では、マーク// Fires!されたアサートがトリガーされるが、がtestで実行された場合に限られvector<bool>ます。で実行したときvector<char>(または任意vectorのほかboolの適切なデフォルト以外のときにT割り当てられている)、テストが通過します。

libc ++の実装ではvector<bool>、汎用コードでの動作が異なることによる悪影響を最小限に抑えようとしました。これを実現するために行ったことの1つは、指定されたと同じようにproxy-referenceを作成 vector<T>::const_referenceすることですが、それを介して割り当てることはできません。つまり、libc ++では、ビットのコピーではなく、本質的に内のビットへのポインタです。vector<T>::referencevector<T>::const_referencevector

上記++のlibcにはtest両方のためのパスvector<char>vector<bool>

どんな費用で?

欠点は、質問に示されているように、この拡張機能が検出可能であることです。ただし、実際にはこのエイリアスの正確なタイプを考慮しているプログラムはほとんどなく、より多くのプログラムが動作を考慮しています。

この不適合の動機は何ですか?

汎用コードでのlibc ++クライアントの動作を改善するには、おそらく十分なフィールドテストの後に、C ++業界全体を改善するために、この拡張機能を将来のC ++標準に提案します。

そのような提案はbit_vector、今日のvector<bool>ものとほとんど同じAPIを持っているが、const_referenceここで説明されているようないくつかのアップグレードを伴う新しいコンテナ(たとえば)の形で来るかもしれません。その後、スペシャライゼーションの廃止(および最終的な削除)が行われvector<bool>ます。 bitsetこの部門では、add const_referenceやイテレータのセットなどの小さなアップグレードも使用できます。

つまり、後bitsetから見るとvector<bool>(これは名前を変更する必要がありますbit_vector-または何か)、そのままarrayですvector。そして、アナロジーは、私たちが話しているか否かが当てはまるべきboolvalue_typevectorarray

libc ++の拡張機能として開始されたC ++ 11およびC ++ 14機能の例が複数あります。これが標準の進化の仕方です。実際に実証され 、正フィールドでの経験は、強力な影響力を運びます。既存の仕様を(あるべきように)変更することに関しては、標準の人々は保守的な束です。推測は、正しく推測していると確信している場合でも、国際的に認められた標準を進化させるためのリスクの高い戦略です。


1
質問:@EricNieblerによるプロキシイテレータに関する最近のドラフト提案は、どういうわけかlibc ++拡張を正当化vector<bool>し、より一流の基盤を築くことができるでしょうか?
TemplateRex

備考:私が持っていることを好むだろうvector_bool<Alloc>array_bool<N>の(すべてのビットを反復ランダムアクセスプロキシイテレータを含む)、パックされたバージョンを表すためにvector<bool, Alloc>array<bool, N>。ただし、bitset<N>(そしてその従兄弟boost::dynamic_bitset<Alloc>)は、異なる抽象化、つまりのパックされたバージョンを表しますstd::set<int>。私は、持っていると言う、と思いますので、bit_array<N>およびbit_vector<Alloc>ビットセットフランチャイズへの後継者であるためには、適切と双方向イテレータ(1ビットを超えるのではなく、すべてのビットを反復処理)。これについてどう思いますか?
TemplateRex

5
プロキシイテレータに関する私のドラフトの提案ではvector<bool>、標準準拠のランダムアクセスコンテナを作成します。それはvector<bool>良い考えにはなりません。:-)私はハワードに同意します。それは別のものと呼ばれるべきでした。
Eric Niebler、2015

1
libc ++拡張をオプトアウトして、厳密に準拠する動作を取得する方法がないのはなぜですか?(私は、適合性をデフォルトにすることを求めているわけではなく、移植可能なコードを記述できるようにlibc ++の拡張機能を無効にする方法にすぎません)。ご存知のとおり、私は過去にlibc ++タプル拡張機能に噛まれ、最近はbitset :: const_reference拡張機能に噛まれました。
gnzlbg 2015

5
@gnzlbg:libc ++の初期開発には、限られた量の経済的および時間的リソースが利用可能でした。その後、実装はすべてのユーザーを満足させないように運命づけられました。利用可能なリソースを考慮して、エンジニアリングのトレードオフは、ハッピーユーザーの数を最大化し、C ++コミュニティ全体への利益を最大化するために行われました。あなたの経験について申し訳ありません。あなたが非難したタプル拡張が現在のC ++ 1zワーキングペーパーにあることに気づきました。その問題については、多くの人が恩恵を受けることができるように無意識のうちに犠牲にしました、そしてそれらの多くはあなたに感謝の借金を負っています。
ハワードヒナント2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.