Чтобы справиться с этим затруднением, функция vector::operator[]
возвращает объект, который имитирует ссылку на отдельный бит — так называемый промежуточный объект. Для использования STL не обязательно понимать, как работают промежуточные объекты, но вообще это весьма полезная идиома C++. Дополнительная информация о промежуточных объектах приведена в совете 30 «More Effective C++», а также в разделе «Паттерн Proxy» книги « Приемы объектно-ориентированного проектирования » [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
не удастся передать функции C, получающей массив bool
(см. совет 16), но это не удалось бы сделать и с vector
из-за отсутствия переносимого способа получения данных vector
. (Прием, продемонстрированный для vector
в совете 16, не компилируется для vector
, поскольку он зависит от возможности получения на тип элемента, хранящегося в векторе, — как упоминалось выше, vector
не содержит bool
.)
Второй альтернативой для vector
является bitset
. Вообще говоря, bitset
не является стандартным контейнером STL, но входит в стандартную библиотеку C++. В отличие от контейнеров STL, размер bitset
(количество элементов) фиксируется на стадии компиляции, поэтому операции вставки-удаления элементов не поддерживаются. Более того, поскольку bitset
не является контейнером STL, в нем отсутствует поддержка итераторов. Тем не менее bitset
, как и vector
, использует компактное представление каждого элемента одним битом, поддерживает функцию flip
контейнера vector
и ряд других специальных функций, имеющих смысл в контексте битовых множеств. Если вы переживете без итераторов и динамического изменения размеров, вероятно, bitset
хорошо подойдет для ваших целей.
А теперь вернемся к благородному, но неудачному эксперименту, из-за которого появился «псевдоконтейнер» vector
. Я уже упоминал о том, что промежуточные объекты часто используются при программировании на C++. Члены Комитета по стандартизации C++ знали об этом, поэтому они решили создать vector
как наглядный пример контейнера, доступ к элементам которого производится через промежуточные объекты. Предполагалось, что при наличии такого примера в Стандарте у программистов появится готовый образец для построения собственных аналогов.
В итоге выяснилось, что создать контейнер с промежуточными объектами, удовлетворяющий всем требованиям к контейнеру STL, невозможно. Так или иначе, следы этой неудачной попытки сохранились в Стандарте. Можно долго гадать, почему vector
был сохранен, но с практической точки зрения это несущественно. Главное — помните, что vector
не удовлетворяет требованиям к контейнерам STL, что им лучше не пользоваться и что существуют альтернативные структуры данных deque
и bitset
, почти всегда способные заменить vector
.
Читать дальше