На этом шаге мы рассмотрим особенности представления и использования класса vector<bool>.
Для векторов, содержащих элементы логического типа, в стандартной библиотеке C++ определена специализированная разновидность класса vector. Это было сделано для того, чтобы оптимизированная версия занимала меньше места, чем стандартная реализация vector для типа bool. В стандартной реализации для каждого элемента резервируется минимум 1 байт. Во внутреннем представлении специализированной реализации vector<bool> каждый элемент обычно представляется одним битом, поэтому она занимает в восемь раз меньше памяти. Впрочем, оптимизация не дается даром: в C++ минимальное адресуемое значение должно иметь размер минимум 1 байт. Следовательно, специализированная версия требует специальной обработки ссылок и итераторов.
В результате vector<bool> не удовлетворяет всем требованиям других векторов (в частности, значение vector<bool>preference не является полноценным l-значением, а итератор vector<bool>::iterator не является итератором произвольного доступа). Следовательно, код шаблона работает с векторами любого типа, за исключением bool. Вдобавок класс vector<bool> может уступать обычным реализациям по скорости работы, потому что операции с элементами приходится преобразовывать в операции с битами. Впрочем, внутреннее устройство vector<bool> зависит от реализации, поэтому эффективность (как скорость работы, так и затраты памяти) может быть разной.
Учтите, что класс vector<bool> представляет собой нечто большее, чем специализацию класса vector<> для bool. В него также включена поддержка специальных операций, упрощающих работу с флагами и наборами битов.
Размер контейнера vector<bool> изменяется динамически, поэтому его можно рассматривать как битовое поле с динамическим размером. Иначе говоря, вы можете добавлять и удалять биты. Если вы работаете с битовым полем, имеющим статический размер, вместо класса vector<bool> лучше использовать bitset.
Дополнительные операции контейнера vector<bool> перечислены в таблице 1. Операция flip(), производящая логическую инверсию, может применяться как ко всем битам, так и к отдельному биту вектора.
| Операция | Описание | 
|---|---|
| c.flip() | Инвертирует все логические элементы | 
| m[idx].flip() | Инвертирует логический элемент с заданным индексом | 
| m[idx] = val | Присваивает значение логическому элементу с индексом idx (присваивание одного бита) | 
| m[idxl] = m[idx2] | Присваивает значение элемента с индексом idx2 элементу с индексом idxl | 
Обратите внимание на возможность вызова flip() для одного логического элемента. На первый взгляд это выглядит странно, потому что оператор индексирования возвращает значение типа bool, а вызов flip() для этого базового типа невозможен. В данном случае класс vector<bool> использует стандартную методику, основанную на применении так называемых заместителей: для vector<bool> возвращаемое значение оператора индексирования (и других операторов, возвращающих элементы) оформлено в виде вспомогательного класса. Если вы хотите, чтобы возвращаемое значение интерпретировалось как значение типа bool, используется автоматическое преобразование типа. Для других операций определяются функции класса. Соответствующая часть объявления vector<bool> выглядит так:
namespace std {
  class vector<bool> { public:
    // Вспомогательный тип для оператора индексирования 
    class reference {
      .   .   .   .   .
      public:
        // Автоматическое преобразование типа к bool 
        operator bool() const;
        // Присваивание
        reference& operator= (const bool);
        reference& operator= (const reference&);
        // Инверсия бита 
        void flip();
    }
    .    .    .    .
    // Операции обращения к элементам
    // - возвращается тип reference вместо bool
    reference operator[] (size_type n);
    reference at(size_type n);
    reference front();
    reference back();
    .    .    .    .
  }    
}
Как видно из этого фрагмента, все функции обращения к элементам возвращают тип reference. Следовательно, вы также можете использовать следующие команды:
c.front().flip(); // Инверсия первого логического элемента c.at(5) = c.back(); // Присвоить последний элемент элементу с индексом 5
Как обычно, вызывающая сторона должна проследить за тем, чтобы в векторе существовали первый, последний и шестой элементы.
Внутренний тип reference используется только для неконстантных контейнеров типа vector<bool>. Константные функции обращения к элементам возвращают обычные значения типа bool.
Со следующего шага мы начнем рассматривать деки.