Почему vector не является контейнером STL?

пункт 18 книги Скотта Мейерса эффективный STL: 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>?

может кто-нибудь объяснить это?

6 ответов


по причинам оптимизации пространства стандарт C++ (начиная с C++98) явно вызывает vector<bool> как специальный стандартный контейнер, где каждый bool использует только один бит пространства, а не один байт, как обычный bool (реализация своего рода "динамического битового набора"). В обмен на эту оптимизацию он не предлагает все возможности и интерфейс обычного стандартного контейнера.

в этом случае, поскольку вы не можете взять адрес бит в байте, такие вещи, как operator[] Не могу вернуть bool& но вместо этого верните прокси-объект,который позволяет манипулировать конкретным битом. Поскольку этот прокси-объект не bool&, вы не можете назначить его адрес bool* как вы могли бы с результатом такого вызова оператора на" нормальный " контейнер. В свою очередь это означает, что bool *pb =&v[0]; недопустимый код.

С другой стороны deque не имеет такой специализации, поэтому каждый bool принимает байт, и вы можете взять адрес возвращаемое значение от operator[].

наконец, обратите внимание, что реализация стандартной библиотеки MS (возможно) неоптимальна в том, что она использует небольшой размер куска для deques, что означает, что использование deque в качестве замены не всегда является правильным ответом.


vector<bool> содержит логические значения в сжатом виде, используя только один бит для значения (а не 8, как массивы bool []). Невозможно вернуть ссылку на бит в C++, поэтому существует специальный вспомогательный тип "bit reference", который предоставляет вам интерфейс к некоторому биту в памяти и позволяет использовать стандартные операторы и приведения.


проблема в том, что vector<bool> возвращает прокси-объект ссылки вместо истинной ссылки, так что код стиля C++98 bool * p = &v[0]; не скомпилируется. Однако, современный C++11 с auto p = &v[0]; можно сделать для компиляции, если operator& и возвращает объект указателя прокси. Говард Хиннант написал блоге детализация алгоритмических улучшений при использовании таких прокси-ссылок и указателей.

Скотт Мейерс имеет длинный пункт 30 в Более Эффективный C++ о прокси-классы. Вы можете пройти долгий путь до почти имитируйте встроенные типы: для любого заданного типа T пара прокси (например,reference_proxy<T> и iterator_proxy<T>) можно сделать взаимосогласованными в том смысле, что reference_proxy<T>::operator&() и iterator_proxy<T>::operator*() являются обратными друг другу.

однако в какой-то момент нужно сопоставить прокси-объекты, чтобы вести себя как T* или T&. Для прокси итератора можно перегрузить operator->() и получить доступ к шаблону 'S без переопределение все функции. Однако для ссылочных прокси вам нужно будет перегрузить operator.(), и это не разрешено в текущем C++ (хотя Sebastian Redl представил такое предложение на BoostCon 2013). Вы можете сделать многословную работу вокруг, как .get() член внутри ссылочного прокси-сервера или реализовать все 'ы внутри ссылки (это то, что делается для vector<bool>::bit_reference), но это либо потеряет встроенный синтаксис, либо введет пользовательские преобразования, которые не имеют встроенной семантики для преобразования типов (вы можете иметь не более одного пользовательского преобразования на аргумент).

TL; DR: нет vector<bool> не является контейнером, потому что стандарт требует реальной ссылки, но его можно заставить вести себя почти как контейнер, по крайней мере, намного ближе с C++11 (auto), чем в C++98.


Это происходит от http://www.cplusplus.com/reference/vector/vector-bool/

вектор bool это специализированная версия vector, которая используется для элементов типа bool и оптимизирует пространство.

Он ведет себя как неспециализированная версия vector, с следующие изменения:

  • хранилище не обязательно является массивом значений bool, но реализация библиотеки может оптимизировать хранилище таким образом что каждое значение
    хранится в одном бите.
  • элементы не создаются с помощью объекта распределителя, но их значение непосредственно устанавливается на соответствующий бит во внутреннем хранилище.
  • функция-член флип и новая подпись для свопа членов.
  • специальный тип члена, ссылка, класс, который обращается к отдельным битам во внутреннем хранилище контейнера с интерфейсом, который
    эмулирует ссылку bool. И наоборот, член const_reference тип обычного типа bool.
  • типы указателей и итераторов, используемые контейнером, не обязательно являются ни указателями, ни соответствующими итераторами, хотя они
    имитирует большую часть их ожидаемого поведения.

эти изменения предоставляют причудливый интерфейс для этой специализации и предпочтение оптимизации памяти по сравнению с обработкой (которая может или не подходит ваши требования.) В любом случае, невозможно создать экземпляр неспециализированный шаблон вектора для bool напрямую. Способы избегайте использования другого типа (char, unsigned char) или контейнер (например, deque) для использования типов обертки или дальнейшей специализации для определенным allocator типов.

bitset-это класс, который предоставляет аналогичную функциональность для фиксированного размера массивы битов.


посмотрите, как это реализовано. STL строит значительно на шаблонах, и поэтому заголовки содержат код, который они делают.

посмотреть stdc++ реализация здесь.

также интересно, хотя не соответствующий STL битовый вектор является llvm:: BitVector С здесь.

суть llvm::BitVector - это вложенный класс с именем reference и соответствующий оператор перегрузка, чтобы сделать BitVector подобно vector С некоторыми ограничениями. Приведенный ниже код является упрощенным интерфейсом, чтобы показать, как BitVector скрывает класс с именем reference чтобы реальная реализация почти вела себя как реальный массив bool без использования 1 байта для каждого значения.

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> это заставляет его вести себя по-разному в некоторые случаях.


многие считают vector<bool> специализация будет ошибкой.

в статье "устаревшие рудиментарные части библиотеки в C++17"
Есть предложение пересмотреть вектор частичной специализации.

существует долгая история частичной специализации bool СТД:: вектор не удовлетворяя требования к контейнера, и внутри в частности, его итераторы, не удовлетворяющие требованиям случайного доступ итератор. Предыдущая попытка осудить этот контейнер была отклонил для C++11, N2204.


одной из причин отказа является то, что неясно, что это будет хочу охаять конкретной специализации шаблона. Что можно было бы рассмотреть этот вопрос с осторожной формулировкой. Более большая проблема заключается в том, что (упакованный) специализация вектора предлагает важное оптимизация, которую искренне ищут клиенты стандартной библиотеки, но больше не будет доступен. Маловероятно, что мы сможем охаять этой части стандарта до замены объекта предлагаемые и принятые, такие как N2050. К сожалению, таких нет пересмотренные предложения, предлагаемые в настоящее время эволюции библиотеки рабочая группа.