Ссылки на отдельные биты тоже запрещены, что представляет определенную проблему для дизайна интерфейса vector, поскольку функция vector::operator[]
должна возвращать значение типа Т&. Если бы контейнер vector действительно содержал bool, этой проблемы не существовало бы, но вследствие особенностей внутреннего представления функция vector::operator[]
должна вернуть несуществующую ссылку на отдельный бит.
Чтобы справиться с этим затруднением, функция vector::operator[]
возвращает объект, который имитирует ссылку на отдельный бит — так называемый промежуточный объект. Для использования STL не обязательно понимать, как работают промежуточные объекты, но вообще это весьма полезная идиома С++. Дополнительная информация о промежуточных объектах приведена в совете 30 «More Effective С++», а также в разделе «Паттерн Ргоху» книги «Приемы объектно-ориентированного проектирования» [6]. На простейшем уровне vector выглядит примерно так:
template
vector {
public:
class reference {…};// Класс, генерирующий промежуточные
// объекты для ссылок на отдельные биты
reference operator[](size_type n); // operator[] возвращает
… // промежуточный объект
};
Теперь понятно, почему следующий фрагмент не компилируется: vector v;
bool *pb=&v[0]; // Ошибка! Выражение в правой части относится к типу
// vector::reference*, а не bool*
А раз фрагмент не компилируется, vector
не удовлетворяет требованиям к контейнерам STL. Да, специфика vector
особо оговорена в Стандарте; да, этот контейнер почти удовлетворяет требованиям к контейнерам STL, но «почти» не считается. Чем больше вы напишете шаблонов, предназначенных для работы с STL, тем глубже вы осознаете эту истину. Уверяю вас, наступит день, когда написанный вами шаблон будет работать лишь в том случае, если получение адреса элемента контейнера дает указатель на тип элемента; и когда этот день придет, вы наглядно ощутите разницу между контейнером и почти контейнером.
Спрашивается, почему же vector
присутствует в Стандарте, если это не контейнер? Отчасти это связано с одним благородным, но неудачным экспериментом, но позвольте мне ненадолго отложить эту тему и заняться более насущным вопросом. Итак, от vector
следует держаться подальше, потому что это не контейнер — но что же делать, когда вам действительно понадобится вектор логических величин?
В стандартную библиотеку входят два альтернативных решения, которые подходят практически для любых ситуаций. Первое решение — deque
. Контейнер deque обладает практически всеми возможностями vector (за исключением разве что reserve и capacity), но при этом deque является полноценным контейнером STL, содержащим настоящие значения bool. Конечно, внутренняя память deque не образует непрерывный блок, поэтому данные deque не удастся передать функции С, получающей массив bool (см. совет 16), но это не удалось бы сделать и с vector из-за отсутствия переносимого способа получения данных vector. (Прием, продемонстрированный для vector в совете 16, не компилируется для vector, поскольку он зависит от возможности получения на тип элемента, хранящегося в векторе, — как упоминалось выше, vector не содержит bool.)