Руководство по стандартной библиотеке шаблонов STL
Структура библиотеки
Библиотека содержит пять основных видов компонентов: - алгоритм (algorithm): определяет вычислительную процедуру.
- контейнер (container): управляет набором объектов в памяти.
- итератор (iterator): обеспечивает для алгоритма средство доступа к содержимому контейнера.
- функциональный объект (function object): инкапсулирует функцию в объекте для использования другими компонентами.
- адаптер (adaptor): адаптирует компонент для обеспечения различного интерфейса. Такое разделение позволяет нам уменьшить количество компонентов. Например, вместо написания функции поиска элемента для каждого вида контейнера мы обеспечиваем единственную версию, которая работает с каждым из них, пока удовлетворяется основной набор требований.
Следующее описание разъясняет структуру библиотеки. Если программные компоненты сведены в таблицу как трёхмерный массив, где одно измерение представляет различные типы данных (например, int, double), второе измерение представляет различные контейнеры (например, вектор, связный список, файл), а третье измерение представляет различные алгоритмы с контейнерами (например, поиск, сортировка, перемещение по кругу) , если i, j и k - размеры измерений, тогда должно быть разработано i* j *k различных версий кода. При использовании шаблонных функций, которые берут параметрами типы данных, нам нужно только j * k версий. Далее, если заставим наши алгоритмы работать с различными контейнерами, то нам нужно просто j+k версий. Это значительно упрощает разработку программ, а также позволяет очень гибким способом использовать компоненты в библиотеке вместе с определяемыми пользователем компонентами. Пользователь может легко определить специализированный контейнерный класс и использовать для него библиотечную функцию сортировки. Для сортировки пользователь может выбрать какую-то другую функцию сравнения либо через обычный указатель на сравнивающую функцию, либо через функциональный объект (объект, для которого определён operator()), который сравнивает.
Если пользователю необходимо выполнить передвижение через контейнер в обратном направлении, то используется адаптер reverse_iterator.
Библиотека расширяет основные средства C++ последовательным способом, так что программисту на C/C++ легко начать пользоваться библиотекой. Например, библиотека содержит шаблонную функцию merge (слияние). Когда пользователю нужно два массива a и b объединить в с, то это может быть выполнено так:
int a[1000]; int b[2000]; int c[3000]; ... merge (a, a+1000, b, b+2000, c); Когда пользователь хочет объединить вектор и список (оба - шаблонные классы в библиотеке) и поместить результат в заново распределённую неинициализированную память, то это может быть выполнено так:
vector
Во многих случаях полезно перемещаться через потоки ввода-вывода таким же образом, как через обычные структуры данных. Например, если мы хотим объединить две структуры данных и затем сохранить их в файле, было бы хорошо избежать создания вспомогательной структуры данных для хранения результата, а поместить результат непосредственно в соответствующий файл. Библиотека обеспечивает и istream_iterator, и ostream_iterator шаблонные классы, чтобы многие из библиотечных алгоритмов могли работать с потоками ввода-вывода, которые представляют однородные блоки данных. Далее приводится программа, которая читает файл, состоящий из целых чисел, из стандартного ввода, удаляя все числа, делящиеся на параметр команды, и записывает результат в стандартный вывод:
main(int argc, char** argv) { if(argc != 2) throw("usage: remove_if_divides integer\n "); remove_copy_if(istream_iterator(cin), istream_iterator(), ostream_iterator(cout, "\n"), not1(bind2nd (modulus(), atoi(argv[1])))); } Вся работа выполняется алгоритмом remove_copy_if, который читает целые числа одно за другим, пока итератор ввода не становится равным end-of-stream (конец-потока) итератору, который создаётся конструктором без параметров. (Вообще все алгоритмы работают способом "отсюда досюда", используя два итератора, которые показывают начало и конец ввода.) Потом remove_copy_if записывает целые числа, которые выдерживают проверку, в выходной поток через итератор вывода, который связан с cout. В качестве предиката remove_copy_if использует функциональный объект, созданный из функционального объекта modulus
Несколько более реалистичный пример - фильтрующая программа, которая берёт файл и беспорядочно перетасовывает его строки.
main(int argc, char**) { if(argc != 1) throw("usage: shuffle\n"); vector v; copy(istream_iterator(cin),istream_iterator(), inserter(v, v.end())); random_shuffle(v.begin(), v.end()); copy(v.begin(), v.end(), ostream_iterator(cout)); } В этом примере copy перемещает строки из стандартного ввода в вектор, но так как вектор предварительно не размещён в памяти, используется итератор вставки, чтобы вставить в вектор строки одну за другой. (Эта методика позволяет всем функциям копирования работать в обычном режиме замены также, как в режиме вставки.) Потом random_shuffle перетасовывает вектор, а другой вызов copy копирует его в поток cout.
Требования
Для гарантии совместной работы различные компоненты библиотеки должны удовлетворять некоторым основным требованиям. Требования должны быть общими, насколько это возможно, так что вместо высказывания "класс X должен определить функцию-член operator++()", мы говорим "для любого объекта x класса X определён ++x ". (Не определено, является ли оператор членом или глобальной функцией.) Требования установлены в терминах чётких выражений, которые определяют допустимые условия типов, удовлетворяющих требованиям. Для каждого набора требований имеется таблица, которая определяет начальный набор допустимых выражений и их семантику. Любой обобщённый алгоритм, который использует требования, должен быть написан в терминах допустимых выражений для своих формальных параметров.
Если требуется, чтобы была операция линейного времени сложности, это значит - не хуже, чем линейного времени, и операция постоянного времени удовлетворяет требованию.
В некоторых случаях мы представили семантические требования, использующие код C++. Такой код предназначен как спецификация эквивалентности одной конструкции другой, не обязательно как способ, которым конструкция должна быть реализована (хотя в некоторых случаях данный код, однозначно, является оптимальной реализацией).
Операторы (Operators)
Чтобы избежать избыточных определений operator!= из operator== и operator>, , >= из operator, библиотека обеспечивает следующее:
template inline bool operator!=(const T1& x, const T2& y) { return !(x == y); } template inline bool operator>(const T1& x, const T2& y) { return y < x; } template inline bool operator inline bool operator>=(const T1& x, const T2& y) { return !(x < y); }
Основные компоненты
Этот раздел содержит некоторые основные шаблонные функции и классы, которые используются в остальной части библиотеки.
Пара (Pair)
Библиотека включает шаблоны для разнородных пар значений. template struct pair { T1 first; T2 second; pair() {} pair(const T1& x, const T2& y) : first(x), second(y) {} }; template inline bool operator==(const pair& x, const pair& y) { return x.first == y.first && x.second == y.second; } template inline bool operator& x, const pair& y) { return x.first < y.first (!(y.first < x.first) && x.second < y.second); } Библиотека обеспечивает соответствующую шаблонную функцию make_pair, чтобы упростить конструкцию пар. Вместо выражения, например: return pair(5, 3.1415926); // явные типы, можно написать return make_pair(5, 3.1415926); // типы выводятся. template inline pair make_pair(const T1& x, const T2& y) { return pair(x, y);
Двунаправленные итераторы (Bidirectional iterators)
Класс или встроенный тип X удовлетворяет требованиям двунаправленного итератора, если к таблице, которая определяет последовательные итераторы, мы добавим следующие строки:
| выражение | возвращаемый тип | семантика исполнения | утверждение/примечание состояние до/после |
| --r | X& | . | до: существует s такое, что r == ++s. после: s - разыменовываемое. --(++r) == r. --r == --s подразумевает r == s. &r == &--r. |
| r-- | X | { X tmp = r; --r; return tmp; } |
. |
Итераторы произвольного доступа (Random access iterators)
Класс или встроенный тип X удовлетворяет требованиям итераторов произвольного доступа, если к таблице, которая определяет двунаправленные итераторы, мы добавим следующие строки:
| выражение | возвращаемый тип | семантика исполнения | утверждение/примечание состояние до/после |
| r += n | X& | { Distance m = n; if(m >= 0) while(m--) ++r; else while(m++) --r; return r; } | . |
| a + n n + a |
X | { X tmp = a; return tmp += n; } |
a + n == n + a. |
| r -= n | X& | return r += -n; | . |
| a - n | X | { X tmp = a; return tmp -= n; } |
. |
| b - a | Distance | . | до: существует значение n типа Distance
такое, что a + n = b. b == a + (b - a). |
| a[n] | обратимый в T | *(a + n) | . |
| a < b | обратимый в bool | b - a > 0 | - это отношение полного упорядочения |
| a > b | обратимый в bool | b < a | > - это отношение полного упорядочения, противоположное . |
| a >= b | обратимый в bool | !(a < b) | . |
| a | обратимый в bool | !(a > b) | . |
Итераторы ввода (Input iterators)
Класс или встроенный тип X удовлетворяет требованиям итератора ввода для значимого типа T, если справедливы следующие выражения:
| выражение | возвращаемый тип | семантика исполнения | утверждение/примечание состояние до/после |
| X(a) | . | . | X(a)- копия a. примечание: предполагается деструктор. |
| X u(a); X u = a; |
. | . | после: u - копия a. |
| u = a | X& | . | после: u - копия a. |
| a == b | обратимый в bool | . | если a - копия b, тогда a == b
возвращает true. == - это отношение эквивалентности в области действия ==. |
| a != b | обратимый в bool | !(a == b) | . |
| *a | обратимый в T | . | до: a - разыменовываемое. если a - копия b, то *a эквивалентно *b. |
| ++r | X& | . | до: r - разыменовываемое. после:r - разыменовываемое или r - законечное. |
| void r++ | void | void ++r | . |
| *r++ | Т | { X tmp = r; ++r; return tmp; } |
. |
Итераторы вывода (Output iterators)
Класс или встроенный тип X удовлетворяет требованиям итератора вывода, если справедливы следующие выражения:
| выражение | возвращаемый тип | семантика исполнения | утверждение/примечание состояние до/после |
| X(a) | . | . | *a = t эквивалентно *X(a) = t. примечание: предполагается деструктор. |
| X u(a); X u = a; |
. | . | . |
| *a = t | результат не используется | . | . |
| ++r | X& | . | . |
| r++ | Х или Х& | . | . |
Итераторы
Так как итераторы - обобщение указателей, их семантика - обобщение семантики указателей в C++. Это гарантирует, что каждая шаблонная функция, которая использует итераторы, работает с обычными указателями. Есть пять категорий итераторов в зависимости от операций, определённых для них: ввода (input iterators), вывода (output iterators), последовательные (forward iterators), двунаправленные (bidirectional iterators) и произвольного доступа (random access iterators.) Последовательные итераторы удовлетворяют всем требованиям итераторов ввода и вывода и могут использоваться всякий раз, когда определяется тот или другой вид. Двунаправленные итераторы удовлетворяют всем требованиям последовательных итераторов и могут использоваться всякий раз, когда определяется последовательный итератор. Итераторы произвольного доступа удовлетворяют всем требованиям двунаправленных итераторов и могут использоваться всякий раз, когда определяется двунаправленный итератор. Имеется дополнительный атрибут, который могли быть иметь последовательные, двунаправленные и произвольного доступа итераторы, то есть они могут быть модифицируемые (mutable) или постоянные (constant) в зависимости от того, ведёт ли себя результат operator* как ссылка или как ссылка на константу.
Постоянные итераторы не удовлетворяют требованиям итераторов вывода.
| Произвольного доступа | --> | Двунаправленные | --> | Последовательные | --- |
|
Точно также, как обычный указатель на массив гарантирует, что имеется значение указателя, указывающего за последний элемент массива, так и для любого типа итератора имеется значение итератора, который указывает за последний элемент соответствующего контейнера. Эти значения называются законечными (past-the-end) значениями. Значения итератора, для которых operator* определён, называются разыменовываемыми (dereferenceable). Библиотека никогда не допускает, что законечные значения являются разыменовываемыми. Итераторы могут также иметь исключительные (singular) значения, которые не связаны ни с каким контейнером. Например, после объявления неинициализированного указателя x (например, int* x;), всегда должно предполагаться, что x имеет исключительное значение указателя. Результаты большинства выражений не определены для исключительных значений. Единственное исключение - присваивание неисключительного значения итератору, который имеет исключительное значение. В этом случае исключительное значение перезаписывается таким же образом, как любое другое значение. Разыменовываемые и законечные значения всегда являются неисключительными.
Итератор j называется доступным (reachable) из итератора i, если и только если имеется конечная последовательность применений operator++ к i, которая делает i==j. Если i и j относятся к одному и тому же контейнеру, тогда или j доступен из i, или i доступен из j, или оба доступны (i == j).
Большинство алгоритмических шаблонов библиотеки, которые работают со структурами данных, имеют интерфейсы, которые используют диапазоны. Диапазон - это пара итераторов, которые указывают начало и конец вычисления. Интервал [i,i) - пустой диапазон; вообще, диапазон [i,j) относится к элементам в структуре данных, начиная с элемента, указываемого i, и до элемента, но не включая его, указываемого j.Диапазон [i,j) допустим, если и только если j доступен из i. Результат применения алгоритмов библиотеки к недопустимым диапазонам не определён.
Все категории итераторов требуют только те функции, которые осуществимы для данной категории со сложностью постоянного времени (амортизированные). Поэтому таблицы требований для итераторов не имеют столбца сложности.
В следующих разделах мы принимаем: a и b - значения X, n - значение типа расстояния Distance, u, tmp и m - идентификаторы, r и s - леводопустимые (lvalues) значения X, t - значение значимого типа T.
Операции с итераторами (Iterator operations)
Так как только итераторы произвольного доступа обеспечивают + и - операторы, библиотека предоставляет две шаблонные функции advance и distance. Эти функции используют + и - для итераторов произвольного доступа (и имеют, поэтому, сложность постоянного времени для них); для итераторов ввода, последовательных и двунаправленных итераторов функции используют ++, чтобы обеспечить реализацию со сложностью линейного времени. advance берет отрицательный параметр n только для итераторов произвольного доступа и двунаправленных итераторов. advance увеличивает (или уменьшает для отрицательного n) итераторную ссылку i на n. distance увеличивает n на число единиц, сколько требуется, чтобы дойти от first до last.
template inline void advance(InputIterator& i, Distance n); template inline void distance(InputIterator first, InputIterator last, Distance& n); distance должна быть функцией 3-х параметров, сохраняющей результат в ссылке вместо возвращения результата, потому что тип расстояния не может быть выведен из встроенных итераторных типов, таких как int*.
Последовательные итераторы (Forward iterators)
Класс или встроенный тип X удовлетворяет требованиям последовательного итератора, если справедливы следующие выражения:
| выражение | возвращаемый тип | семантика исполнения | утверждение/примечание состояние до/после |
| X u; | . | . | примечание: u может иметь исключительное значение. примечание: предполагается деструктор. |
| X() | . | . | примечание: X() может быть исключительным. |
| X(a); | . | . | a == X(a) |
| X u(a); X u = a; |
. | X u; u = a; | после: u == a. |
| a == b | обратимый в bool | . | == - это отношение эквивалентности. |
| a != b | обратимый в bool | !(a == b) | . |
| r = a | X& | . | после: r == a. |
| *a | обратимый в T | . | до: a - разыменовываемое. a == b подразумевает *a == *b. Если X - модифицируемый, то *a = t - допустимо. |
| ++r | X& | . | до: r - разыменовываемое. после: r - разыменовываемое или r - законечное. r == s и r - разыменовываемое подразумевает ++r == ++s. &r == &++r. |
| r++ | X | { X tmp = r; ++ r; return tmp; } |
. |
Примеры использования тегов итераторов
Для всех типов обычных указателей мы можем определить value_type и distance_type с помощью следующего:
template inline T* value_type(const T*) { return (T*) (0); } template inline ptrdiff_t* distance_type(const T*) { return (ptrdiff_t*) (0); } Тогда, если мы хотим осуществить обобщённую функцию reverse, мы пишем следующее: template inline void reverse(BidirectionalIterator first, BidirectionalIterator last) { _reverse(first, last, value_type(first), distance_type(first)); } где _reverse определена следующим образом: template void _reverse(BidirectionalIterator first, BidirectionalIterator last, T*, Distance*) { Distance n; distance(first, last, n); // смотри раздел "Операции с итераторами" --n; while (n > 0) { T tmp = *first; *first++ = *--last; *last = tmp; n -= 2; } } Если имеется дополнительный тип указателя _huge такой, что разность двух указателей _huge имеет тип long long, мы определяем:
template inline T* value_type(const T _huge *) { return (T*) (0); } template inline long long* distance_type(const T _huge *) { return (long long*)(0); } Часто желательно для шаблонной функции выяснить, какова наиболее специфичная категория её итераторного аргумента, так чтобы функция могла выбирать наиболее эффективный алгоритм во время компиляции. Чтобы облегчить это, библиотека вводит классы тегов категорий (category tag), которые используются как теги времени компиляции для выбора алгоритма. Это следущие теги: input_iterator_tag, output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag и random_access_iterator_tag. Каждый итератор i должен иметь выражение iterator_category(i), определённое для него, которое возвращает тег наиболее специфичной категории, который описывает его поведение. Например, мы определяем, что все типы указателей находятся в категории итераторов произвольного доступа:
template inline random_access_iterator_tag iterator_category(const T*) { return random_access_iterator_tag(); } Определяемый пользователем итератор BinaryTreeIterator может быть включен в категорию двунаправленных итераторов следующим образом: template inline bidirectional_iterator_tag iterator_category( const BinaryTreeIterator&) { return bidirectional_iterator_tag(); } Если шаблонная функция evolve хорошо определена для двунаправленных итераторов, но может быть осуществлена более эффективно для итераторов произвольного доступа, тогда реализация выглядит так:
template inline void evolve(BidirectionalIterator first, BidirectionalIterator last) { evolve(first, last, iterator_category(first)); } template void evolve(BidirectionalIterator first, BidirectionalIterator last, bidirectional_iterator_tag) { // ... более универсальный, но менее эффективный алгоритм } template void evolve(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) { // ... более эффективный, но менее универсальный алгоритм }
Примитивы, определённые в библиотеке
Чтобы упростить задачу определения iterator_category, value_type и distance_type для определяемых пользователем итераторов, библиотека обеспечивает следующие предопределённые классы и функции:
// iterator tags (теги итераторов) struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag {}; struct bidirectional_iterator_tag {}; struct random_access_iterator_tag {}; // iterator bases (базовые классы итераторов) template struct input_iterator {}; struct output_iterator {}; // output_iterator не шаблон, потому что у итераторов вывода // не определены ни значимый тип, ни тип расстояния. template struct forward_iterator {}; template struct bidirectional_iterator {}; template struct random_access_iterator {}; // iterator_category (функции категорий итераторов) template inline input_iterator_tag iterator_category(const input_iterator&) { return input_iterator_tag(); } inline output_iterator_tag iterator_category(const output_iterator&) { return output_iterator_tag(); } template inline forward_iterator_tag iterator_category(const forward_iterator&) { return forward_iterator_tag(); } template inline bidirectional_iterator_tag iterator_category(const bidirectional_iterator&) { return bidirectional_iterator_tag(); ) template inline random_access_iterator_tag iterator_category(const random_access_iterator&) { return random_access_iterator_tag(); } template inline random_access_iterator_tag iterator_category(const T*) { return random_access_iterator_tag(); } // value_type of iterator (функции значимого типа итераторов) template inline T* value_type(const input_iterator&) ( return (T*) (0); } template inline T* value_type(const forward_iterator&) { return (T*) (0); } template inline T* value_type(const bidirectional_iterator&) { return (T*) (0); } template inline T* value_type(const random_access_iterator&) { return (T*) (0); } template inline T* value_type(const T*) { return (T*) (0); } // distance_type of iterator (функции типа расстояния итераторов) template inline Distance* distance_type(const input_iterator&) { return (Distance*) (0); } template inline Distance* distance_type(const forward_iterator&) { return (Distance*) (0); } template inline Distance* distance_type(const bidirectional_iterator&) { return (Distance*) (0); } template inline Distance* distance_type(const random_access_iterator&) { return (Distance*) (0); } template inline ptrdiff_t* distance_type(const T*) { return (ptrdiff_t*) (0); } Если пользователь хочет определить двунаправленный итератор для некоторой структуры данных, содержащей double, и такой, чтобы работал с большой (large) моделью памяти компьютера, то это может быть сделано таким определением:
class MyIterator : public bidirectional_iterator { // код, осуществляющий ++, и т.д. }; Тогда нет необходимости определять iterator_category, value_type, и distance_type в MyIterator.
Теги итераторов (Iterator tags)
Чтобы осуществлять алгоритмы только в терминах итераторов, часто бывает необходимо вывести тип значения и тип расстояния из итератора. Для решения этой задачи требуется, чтобы для итератора i любой категории, отличной от итератора вывода, выражение value_type(i) возвращало (T*)(0), а выражение distance_type(i) возвращало (Distance*)(0). Для итераторов вывода эти выражения не требуются.
Арифметические операции (Arithmetic operations)
Библиотека обеспечивает базовые классы функциональных объектов для всех арифметических операторов языка.
template struct plus : binary_function { Т operator()(const T& x, const T& y) const { return x + y; } }; template struct minus : binary_function { Т operator()(const T& x, const T& y) const { return x - y; } }; template struct times : binary_function { Т operator()(const T& x, const T& y) const ( return x * y; } }; template struct divides : binary_function { Т operator()(const T& x, const T& y) const { return x / y; } }; template struct modulus : binary_function { Т operator()(const T& x, const T& y) const { return x % y; } }; template struct negate : unary_function { Т operator()(const T& x) const { return -x; } };
Базовые классы (Base)
Следующие классы предоставляются, чтобы упростить определение типов (typedefs) параметров и результата:
template struct unary_function { typedef Arg argument_type; typedef Result result_type; }: template struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };
Функциональные объекты
Функциональные объекты - это объекты, для которых определён operator(). Они важны для эффективного использования библиотеки. В местах, где ожидается передача указателя на функцию алгоритмическому шаблону, интерфейс установлен на приём объекта с определённым operator(). Это не только заставляет алгоритмические шаблоны работать с указателями на функции, но также позволяет им работать с произвольными функциональными объектами. Использование функциональных объектов вместе с функциональными шаблонами увеличивает выразительную мощность библиотеки также, как делает результирующий код более эффективным. Например, если мы хотим поэлементно сложить два вектора a и b, содержащие double, и поместить результат в a, мы можем сделать зто так:
transform(a.begin(), a.end(), b.begin(), a.begin(), plus()); Если мы хотим отрицать каждый элемент a, мы можем сделать это так:
transform(a.begin(), a.end(), a.begin(), negate()); Соответствующие функции вставят сложение и отрицание. Чтобы позволить адаптерам и другим компонентам манипулировать функциональными объектами, которые используют один или два параметра, требуется, чтобы они соответственно обеспечили определение типов (typedefs) argument_type и result_type для функциональных объектов, которые используют один параметр, и first_argument_type, second_argument_type и result_type для функциональных объектов, которые используют два параметра.
Логические операции (Logical operations)
template struct logical_and : binary_function { bool operator()(const T& x, const T& y) const { return x && y; } };
template struct logical_or : binary_function { bool operator()(const T& x, const T& y) const { return x y; } };
template struct logical_not : unary_function { bool operator()(const T& x) const { return !x; } };
Сравнения (Comparisons)
Библиотека обеспечивает базовые классы функциональных объектов для всех операторов сравнения языка
template struct equal_to : binary_function { bool operator()(const T& x, const T& y) const { return x == y; } }; template struct not_equal_to : binary_function { bool operator()(const T& x, const T& y) const { return x != y; } }; template struct greater : binary_function { bool operator()(const T& x, const T& y) const { return x > y; } }; template struct less : binary_function { bool operator()(const T& x, const T& y) const { return x < y; } }; template struct greater_equal : binary_function { bool operator()(const T& x, const T& y) const { return x >= y; } }; template struct less_equal : binary_function { bool operator()(const T& x, const T& y) const { return x
Распределитель по умолчанию (The default allocator)
template class allocator { public: typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; typedef size_t size_type; typedef ptrdiff_t difference_type; allocator(); ~allocator(); pointer address(reference x); const_pointer const_address(const_reference x); pointer allocate(size_type n); void deallocate(pointer p); size_type init_page_size(); size_type max_size(); }; class allocator { public: typedef void* pointer; allocator(); ~allocator(); }; Предполагается, что в дополнение к allocator поставщики библиотеки обеспечивают распределители для всех моделей памяти.
Распределители
Одна из общих проблем в мобильности - это способность инкапсулировать информацию относительно модели памяти. Эта информация включает типы указателей, тип их разности, тип размера объектов в этой модели памяти, также как её примитивы выделения и освобождения памяти.
STL принимается за эту проблему, обеспечивая стандартный набор требований для распределителей (allocators), являющихся объектами, которые инкапсулируют эту информацию. Все контейнеры в STL параметризованы в терминах распределителей. Это значительно упрощает задачу взаимодействия с многочисленными моделями памяти.
Требования распределителей (Allocator requirements)
В следующей таблице мы предполагаем, что X - класс распределителей для объектов типа T, a - значение X, n имеет тип X::size_type, p имеет тип X::pointer, r имеет тип X::reference и s имеет тип X::const_reference.
Все операции c распределителями, как ожидается, сводятся к постоянному времени.
| выражение | возвращаемый тип | утверждение/примечание состояние до/после |
| X::value_type | Т | . |
| X::reference | леводопустимое значение T (lvalue of T) | . |
| X::const_reference | const lvalue of T | . |
| X::pointer | указатель на тип T | результатом operator* для значений X::pointer является reference. |
| X::const_pointer | указатель на тип const T | результат operator* для значений X::const_pointer - const_reference; это - тот же самый тип указателя, как X::pointer, в частности, sizeof(X::const_pointer) == sizeof(X::pointer). |
| X:: size_type | беззнаковый целочисленный тип | тип, который может представлять размер самого большого объекта в модели памяти. |
| X::difference_type | знаковый целочисленный тип | тип, который может представлять разность между двумя любыми указателями в модели памяти. |
| X a; | . | примечание: предполагается деструктор. |
| a.address(r) | указатель | *(a.address(r)) == r. |
| a.const_address(s) | const_pointer | *(a.address(s)) == s. |
| a.allocate(n) | X::pointer | память распределяется для n объектов типа T, но объекты не создаются. allocate может вызывать соответствующее исключение. |
| a.deallocate(p) | результат не используется | все объекты в области, указываемой p, должны быть уничтожены до этого запроса. |
| construct(p, a) | void | после: *p == a. |
| destroy(p) | void | значение, указываемое p, уничтожается. |
| a.init_page_size() | X::size_type | возвращённое значение - оптимальное значение для начального размера буфера данного типа. Предполагается, что если k возвращено функцией init_page_size, t - время конструирования для T, и u - время, которое требуется для выполнения allocate(k), тогда k * t будет намного больше, чем u. |
| a.max_size() | X::size_type | наибольшее положительное значение X::difference_type |
Для любого шаблона распределителя Alloc имеется определение для типа void. У Alloc определены только конструктор, деструктор и Alloc::pointer. Преобразования определены из любого Alloc::pointer в Alloc::pointer и обратно, так что для любого p будет p == Alloc::pointer(Alloc::pointer(p)).
Ассоциативные контейнеры (Associative containers)
Все они берут в качестве параметров Key (ключ) и упорядочивающее отношение Compare, которое вызывает полное упорядочение по элементам Key. Кроме того, map и multimap ассоциируют произвольный тип T с Key. Объект типа Compare называется сравнивающим объектом (comparison object) контейнера.
В этом разделе, когда мы говорим о равенстве ключей, мы подразумеваем отношение эквивалентности, обусловленное сравнением и не (not) operator== для ключей. То есть считается, что два ключа k1 и k2 являются равными, если для сравнивающего объекта comp истинно comp(k1, k2) == false && comp(k2, k1) == false.
Ассоциативный контейнер поддерживает уникальные ключи (unique keys), если он может содержать, самое большее, один элемент для каждого значения ключа. Иначе он поддерживает равные ключи (equal keys). set и map поддерживают уникальные ключи. multiset и multimap поддерживают равные ключи.
Для set и multiset значимый тип - тот же самый, что и тип ключа. Для map и multimap он равен pair.
iterator ассоциативного контейнера относится к категории двунаправленного итератора. insert не влияет на действительность итераторов и ссылок контейнера, а erase делает недействительными только итераторы и ссылки на стёртые элементы.
В следующей таблице обозначается: X - класс ассоциативного контейнера, a - значение X, a_uniq - значение X, когда X поддерживает уникальные ключи, a a_eq - значение X, когда X поддерживает многократные ключи, i и j удовлетворяют требованиям итераторов ввода и указывают на элементы value_type, [i, j) - допустимый диапазон, p - допустимый итератор для a, q - разыменовываемый итератор для a, [q1, q2) - допустимый диапазон в a, t - значение X::value_type и k - значение X::key_type.
| X::key_type | Key | . | время компиляции | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| X::key_compare | Compare | по умолчанию less| время компиляции | X::value_compare
| тип бинарного предиката | то же, что key_compare для set и multiset; | отношение упорядочения пар, вызванное первым компонентом (т.е. Key), для map и multimap. время компиляции | X(c) | X a(c); . | создает пустой контейнер; | использует с как объект сравнения. постоянная | X() | X a; . | создает пустой контейнер; | использует Compare() как объект сравнения. постоянная | X(i,j,c) | X a(i,j,c); . | cоздает пустой контейнер и вставляет в него элементы из диапазона [i, j); | использует с как объект сравнения. вообще NlogN (N - расстояние от i до j); | линейная, если [i, j) отсортирован value_comp() X(i,j) | X a(i,j); . | то же, что выше, но использует Compare() как объект сравнения. | то же, что выше | a.key_comp()
| X::key_compare
| возвращает объект сравнения, из которого а был создан. | постоянная | a.value_comp() | X::value_compare
| возвращает объект value_compare, созданный из объекта сравнения. | постоянная | a_uniq.insert(t)
| pair | вставляет t, если и только если в контейнере нет элемента с ключом, равным ключу t. Компонент bool возвращенной пары показывает, происходит ли вставка, а компонент пары iterator указывает на элемент с ключом, равным ключу t. | логарифмическая | a_eq.insert(t)
| iterator
| вставляет t и возвращает итератор, указывающий на вновь вставленный элемент. | логарифмическая | a.insert(p, t)
| iterator
| вставляет t, если и только если в контейнерах с уникальными ключами нет элемента с ключом, равным ключу t; всегда вставляет t в контейнеры с дубликатами. | всегда возвращает итератор, указывающий на элемент с ключом, равным ключу t. итератор p - подсказка, указывающая, где вставка должна начать поиск. вообще логарифмическая, но сводится к постоянной, если t вставлен прямо перед p. | a.insert(i, j)
| результат не используется | вставляет в контейнер элементы из диапазона [i, j); | вообще Nlog(size()+N) (N - расстояние от i до j); | линейная, если [i, j) отсортирован согласно value_comp() a.erase(k)
| size_type
| стирает все элементы в контейнере с ключом, равным k. | возвращает число уничтоженных элементов. log(size()) + count(k)
| a.erase(q)
| результат не используется | стирает элемент, указанный q. | сводится к постоянной | a.erase(ql, q2)
| результат не используется | стирает все элементы в диапазоне [ql, q2). | log(size())+ N, где N - расстояние от ql до q2. | a.find(k)
| iterator; | const_iterator для константы a возвращает итератор, указывающий на элемент с ключом, равным k, или a.end(), если такой элемент не найден. | логарифмическая | a.count(k)
| size_type
| возвращает число элементов с ключом, равным k. | log(size()) + count(k)
| a.lower_bound(k)
| iterator; | const_iterator для константы a возвращает итератор, указывающий на первый элемент с ключом не меньше, чем k. | логарифмическая | a.upper_bound(k)
| iterator; | const_iterator для константы a возвращает итератор, указывающий на первый элемент с ключом больше, чем k. | логарифмическая | a.equal_range(k)
| pair | pair эквивалент make_pair(lower_boud(k), upper_bound (k)). | логарифмическая | |
Двусторонняя очередь (Deque)
deque - вид последовательности, которая, подобно вектору, поддерживает итераторы произвольного доступа. Кроме того она поддерживает операции вставки и стирания в начале или в конце за постоянное время; вставка и стирание в середине занимают линейное время. Как с векторами, управление памятью обрабатывается автоматически.
template class Allocator = allocator> class deque { public: // typedefs: typedef iterator; typedef const_iterator; typedef Allocator::pointer pointer; typedef Allocator ::reference reference; typedef Allocator::const_reference const_reference; typedef size_type; typedef difference_type; typedef Т value_type; typedef reverse_iterator; typedef const_revcrse_iterator; // размещение/удаление: deque(); deque(size_type n, const T& value = T()); deque(const deque& x); template deque(InputIterator first, InputIterator last); ~deque(); deque& operator=(const deque& x); void swap(deque& x); // средства доступа: iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin(); reverse_iterator rend(); const_reverse_iterator rend(); size_type size() const; size_type max_size() const; bool empty() const; reference operator[](size_type n); const_reference operator[](size_type n) const; reference front(); const_reference front() const; reference back(); const_reference back() const; // вставка/стирание: void push_front(const T& x); void push_back(const T& x); iterator insert(iterator position, const T& x = T()); void insert(iterator position, size_type n, const T& x); template void insert(iterator position, InputIterator first, InputIterator last); void pop_front(); void pop_back(); void erase(iterator position); void erase(iterator first, iterator last); }; template bool operator==(const deque& x, const deque& y); template bool operator& x, const deque& y); iterator - итератор произвольного доступа, ссылающийся на T.
Точный тип зависит от исполнения и определяется в Allocator.
const_iterator - постоянный итератор произвольного доступа, ссылающийся на const T. Точный тип зависит от исполнения и определяется в Allocator. Гарантируется, что имеется конструктор для const_iterator из iterator.
size_type - беззнаковый целочисленный тип. Точный тип зависит от исполнения и определяется в Allocator.
difference_type - знаковый целочисленный тип. Точный зависит от исполнения и определяется в Allocator.
insert (вставка) в середину двусторонней очереди делает недействительными все итераторы и ссылки двусторонней очереди. insert и push (помещение) с обоих концов двусторонней очереди делают недействительными все итераторы двусторонней очереди, но не влияют на действительность всех ссылок на двустороннюю очередь. В худшем случае вставка единственного элемента в двустороннюю очередь занимает линейное время от минимума двух расстояний: от точки вставки - до начала и до конца двусторонней очереди. Вставка единственного элемента либо в начало, либо в конец двусторонней очереди всегда занимает постоянное время и вызывает единственный запрос конструктора копии T. То есть двусторонняя очередь особенно оптимизирована для помещения и извлечения элементов в начале и в конце.
erase (стирание) в середине двусторонней очереди делает недействительными все итераторы и ссылки двусторонней очереди. erase и pop (извлечение) с обоих концов двусторонней очереди делают недействительными только итераторы и ссылки на стёртый элемент. Число вызовов деструктора равно числу стёртых элементов, а число вызовов оператора присваивания равно минимуму из числа элементов перед стёртыми элементами и числа элементов после стёртых элементов.
Контейнеры
В следующей таблице мы полагаем, что X - контейнерный класс, содержащий объекты типа T, a и b - значения X, u - идентификатор, r - значение X&.
| выражение | возвращаемый тип | семантика исполнения | утверждение/примечание состояние до/после |
сложность |
| X::value_type | Т | . | . | время компиляции |
| X::reference | . | . | . | время компиляции |
| X::const_refe rence |
. | . | . | время компиляции |
| X::pointer | тип указателя, указывающий на X::reference | . | указатель на T в модели памяти, используемой контейнером | время компиляции |
| X::iterator | тип итратора, указывающий на X::reference | . | итератор любой категории, кроме итератора вывода. | время компиляции |
| X::const_iter ator |
тип итератора, указывающий на X:: const_reference |
. | постоянный итератор любой категории, кроме итератора вывода. | время компиляции |
| X::difference _type |
знаковый целочисленный тип | . | идентичен типу расстояния X::iterator и X::const_iterator | время компиляции |
| X::size_type | беззнаковый целочисленный тип | . | size_type может представлять любое неотрицательное значение difference_type | время компиляции |
| X u; | . | . | после: u.size() == 0. | постоянная |
| X() | . | . | X().size() == 0. | постоянная |
| X(a) | . | . | a == X(a). | линейная |
| X u(a); X u == a; |
. | X u; u = a; | после: u == a. | линейная |
| (&a)->~X() | результат не используется | . | после: a.size() == 0. примечание: деструктор применяется к каждому элементу a, и вся память возвращается. |
линейная |
| a.begin() | iterator; const_iterator для постоянного a |
. | . | постоянная |
| a.end() | iterator; const_iterator для постоянного a |
. | . | постоянная |
| a == b | обратимый в bool | a.size() == b.size() && equal(a.begin(), a.end(), b.begin()) |
== - это отношение эквивалентности. примечание: eqial определяется в разделе алгоритмов. |
линейная |
| a != b | обратимый в bool | !(a == b) | . | линейная |
| r = a | X& | if(&r != &a) { (&r)-> X::~X(); new (&r) X(a); return r; } |
после: r == a. | линейнaя |
| a.size() | size_type | size_type n = 0; distance (a.begin(), a.end(), n); return n; |
. | постоянная |
| a.max_size() | size_type | . | size() самого большого возможного контейнера. | постоянная |
| a.empty() | обратимый в bool | a.size() == 0 | . | постоянная |
| a < b | обратимый в bool | lexicographical _compare (a.begin(), a.end(), b.begin(), b.end()) |
до: определён для значений T. - отношение полного упорядочения. lexicographical _compare определяется в разделе алгоритмов. |
линейная |
| a > b | обратимый в bool | b < a | . | линейнaя |
| a | обратимый в bool | !(a > b) | . | линейная |
| a >= b | обратимый в bool | !(a < b) | . | линейная |
| a.swap(b) | void | swap(a, b) | . | постоянная |
Функция-член size() возвращает число элементов в контейнере. Её семантика определяется правилами конструкторов, вставок и удалений.
begin() возвращает итератор, ссылающийся на первый элемент в контейнере. end() возвращает итератор, который является законечным.
Если тип итератора контейнера принадлежит к категории двунаправленных итераторов или итераторов произвольного доступа, то контейнер называется reversible (обратимым) и удовлетворяет следующим дополнительным требованиям:
| выражение | возвращаемый тип | семантика исполнения | сложность |
| X::reverse _iterator | . | reverse_iterator для итератора произвольного доступа. reverse_bidirectional_iteratoriterator, value_type, reference, difference_type> для двунаправленного итератора | время компиляции |
| X::const_r everse_ite rator |
. | reverse_iterator для итератора произвольного доступа. reverse_bidirectional_iteratorconst_iterator, value_type, const_reference, difference_type> для двунаправленного итератора. |
время компиляции |
| a.rbegin() | reverse_iterator; const_reverse_iter ator для постоянного a |
reverse_iterator(end()) | постоянная |
| a.rend() | reverse_iterator; const_reverse_iter ator для постоянного a |
reverse_iterator(begin()) | постоянная |
Множество с дубликатами (Multiset)
multiset - это ассоциативный контейнер, который поддерживает равные ключи (возможно, содержит множественные копии того же самого значения ключа) и обеспечивает быстрый поиск ключей.
template
сonst_iterator - тот же самый тип, что и iterator.
size_type - целочисленный тип без знака. Точный тип зависит от реализации и определяется в Allocator.
difference_type - целочисленный тип со знаком. Точный тип зависит от реализации и определяется в Allocator.
Множество (Set)
set - это ассоциативный контейнер, который поддерживает уникальные ключи (не содержит ключи с одинаковыми значениями) и обеспечивает быстрый поиск ключей.
template
сonst_iterator - тот же самый тип, что и iterator.
size_type - целочисленный тип без знака. Точный тип зависит от реализации и определяется в Allocator.
difference_type - целочисленный тип со знаком. Точный тип зависит от реализации и определяется в Allocator.
Последовательности (Sequences)
Последовательность - это вид контейнера, который организует конечное множество объектов одного и того же типа в строгом линейном порядке. Библиотека обеспечивает три основных вида последовательных контейнеров: vector (вектор), list (список) и deque (двусторонняя очередь). Она также предоставляет контейнерные адаптеры, которые облегчают создание абстрактных типов данных, таких как стеки или очереди, из основных видов последовательностей (или из других видов последовательностей, которые пользователь может сам определить).
В следующих двух таблицах X - последовательный класс, a - значение X, i и j удовлетворяют требованиям итераторов ввода, [i, j) - допустимый диапазон, n - значение X::size_type, p - допустимый итератор для a, q - разыменовываемый итератор для a, [ql, q2) - допустимый диапазон в a, t - значение X::value_type.
Сложности выражений зависят от последовательностей.
| выражение | возвращаемый тип | утверждение/примечание состояние до/после |
| X(n, t) X a(n, t); | . | после: size() == n. создаёт последовательность с n копиями t. |
| X(i, j) X a(i, j); |
. | после: size() == расстоянию между i и j. создаёт последовательность, равную диапазону [i, j). |
| a.insert(p, t) | iterator | вставляет копию t перед p. возвращаемое значение указывает на вставленную копию. |
| a.insert(p, n, t) | результат не используется | вставляет n копий t перед p. |
| a.insert(p, i, j) | результат не используется | вставляет копии элементов из диапазона [i, j) перед p. |
| a.erase(q) | результат не используется | удаляет элемент, указываемый q. |
| a.erase(ql, q2) | результат не используется | удаляет элементы в диапазоне [ql, q2). |
list нужно использовать, когда имеются частые вставки и удаления из середины последовательности, deque - структура данных для выбора, когда большинство вставок и удалений происходит в начале или в конце последовательности.
Типы iterator и const_iterator для последовательностей должны быть, по крайней мере, из категории последовательных итераторов.
| выражение | возвращаемый тип | семантика исполнения | контейнер |
| a.front() | reference; const_reference для постоянного a | *a.begin() | vector, list, deque |
| a.back() | reference; const_reference для постоянного a |
*a.(--end()) | vector, list, deque |
| a.push_front(t) | void | a.insert(a.begin(), t) | list, deque |
| a.push_back(t) | void | a.insert(a.end(), t) | vector, list, deque |
| a.pop_front () | void | a.erase(a.begin()) | list, deque |
| a.pop_back () | void | a.erase(-- a.end()) | vector, list, deque |
| a[n] | reference; const_reference для постоянного a |
*(a.begin() + n) | vector, deque |
Словарь (Map)
map - ассоциативный контейнер, который поддерживает уникальные ключи (не содержит ключи с одинаковыми значениями) и обеспечивает быстрый поиск значений другого типа T, связанных с ключами.
template
const_iterator - постоянный двунаправленный итератор, указывающий на const value_type. Точный тип зависит от реализации и определяется в Allocator. Гарантируется, что имеется конструктор для const_iterator из iterator.
size_type - целочисленный тип без знака. Точный тип зависит от реализации и определяется в Allocator.
difference_type - целочисленный тип со знаком. Точный тип зависит от реализации и определяется в Allocator.
В дополнение к стандартному набору методов ассоциативных контейнеров, map обеспечивает операцию Allocator::reference operator[](const key_type&). Для словаря m и ключа k запись m[k] семантически эквивалентна (*((m.insert(make_pair(k, T()))).first)).second.
Словарь с дубликатами (Multimар)
multimар - ассоциативный контейнер, который поддерживает равные ключи (возможно, содержит множественные копии того же самого значения ключа) и обеспечивает быстрый поиск значений другого типа T, связанных с ключами.
template
const_iterator - постоянный двунаправленный итератор, указывающий на value_type. Точный тип зависит от реализации и определяется в Allocator. Гарантируется, что имеется конструктор для const_iterator из iterator.
size_type - целочисленный тип без знака. Точный тип зависит от реализации и определяется в Allocator.
difference_type - целочисленный тип со знаком. Точный тип зависит от реализации и определяется в Allocator.
Список (List)
list - вид последовательности, которая поддерживает двунаправленные итераторы и позволяет операции вставки и стирания с постоянным временем в любом месте последовательности, с управлением памятью, обрабатываемым автоматически. В отличие от векторов и двусторонних очередей, быстрый произвольный доступ к элементам списка не поддерживается, но многим алгоритмам, во всяком случае, только и нужен последовательный доступ.
template class Allocator = allocator> class list { public: // определения типов: typedef iterator; typedef const_iterator; typedef Allocator::pointer pointer; typedef Allocator::reference reference; typedef Allocator::const_reference const_reference; typedef size_type; typedef difference_type; typedef Т value_type; typedef reverse_iterator; typedef const_reverse_iterator; // размещение/удаление: list() list(size_type n, const T& value = T()); template list(InputIterator first, InputIterator last); list(const list& x) ; ~list(); list& operator=(const list& x); void swap(list void insert(iterator position, InputIterator first, InputIterator last); void pop_front(); void pop_back(); void erase(iterator position); void erase(iterator first, iterator last); // специальные модифицирующие операции cо списком: void splice(iterator position, list& x); void splice(iterator position, list& x, iterator i); void splice(iterator position, list& x, iterator first, iterator last); void remove(const T& value); template void remove_if(Predicate pred); void unique(); template void unique(BinaryPredicate binary_pred); void merge(list& x); template void merge(list& x, Compare comp); void reverse(); void sort(); template void sort(Compare comp); }; template bool operator==(const list& x, const list& y); template bool operator& x, const list& y); iterator - двунаправленный итератор, ссылающийся на T. Точный тип зависит от исполнения и определяется в Allocator.
const_iterator - постоянный двунаправленный итератор, ссылающийся на const T.
Точный тип зависит от исполнения и определяется в Allocator. Гарантируется, что имеется конструктор для const_iterator из iterator.
size_type - беззнаковый целочисленный тип. Точный тип зависит от исполнения и определяется в Allocator.
difference_type - знаковый целочисленный тип. Точный тип зависит от исполнения и определяется в Allocator.
insert не влияет на действительность итераторов и ссылок. Вставка единственного элемента в список занимает постоянное время, и ровно один раз вызывается конструктор копирования T. Вставка множественных элементов в список зависит линейно от числа вставленных элементов, а число вызовов конструктора копирования T точно равно числу вставленных элементов.
erase делает недействительными только итераторы и ссылки для стёртых элементов. Стирание единственного элемента - операция постоянного времени с единственным вызовом деструктора T. Стирание диапазона в списке занимает линейное время от размера диапазона, а число вызовов деструктора типа T точно равно размеру диапазона.
Так как списки позволяют быструю вставку и стирание в середине списка, то некоторые операции определяются специально для них:
list обеспечивает три операции стыковки, которые разрушительно перемещают элементы из одного списка в другой:
void splice(iterator position, list& x) вставляет содержимое x перед position, и x становится пустым. Требуется постоянное время. Результат не определён, если &x == this.
void splice(iterator position, list& x, iterator i) вставляет элемент, указываемый i, из списка x перед position и удаляет элемент из x. Требуется постоянное время. i - допустимый разыменовываемый итератор списка x. Результат не изменяется, если position == i или position == ++i.
void splice(iterator position, list& x, iterator first, iterator last) вставляет элементы из диапазона [first, last) перед position и удаляет элементы из x.
Требуется постоянное время, если &x == this; иначе требуется линейное время. [first, last) - допустимый диапазон в x. Результат не определён, если position - итератор в диапазоне [first, last).
remove стирает все элементы в списке, указанном итератором списка i, для которого выполняются следующие условия: *i == value, pred(*i) == true. remove устойчиво, то есть относительный порядок элементов, которые не удалены, тот же самый, как их относительный порядок в первоначальном списке. Соответствующий предикат применяется точно size() раз.
unique стирает все, кроме первого элемента, из каждой последовательной группы равных элементов в списке. Соответствующий бинарный предикат применяется точно size() - 1 раз.
merge сливает список аргумента со списком (предполагается, что оба сортированы). Слияние устойчиво, то есть для равных элементов в двух списках элементы списка всегда предшествуют элементам из списка аргумента. x пуст после слияния. Выполняется, самое большее, size() + x.size() - 1 сравнений.
reverse переставляет элементы в списке в обратном порядке. Операция линейного времени.
sort сортирует список согласно operator< или сравнивающему функциональному объекту. Она устойчива, то есть относительный порядок равных элементов сохраняется. Выполняется приблизительно NlogN сравнений, где N равно size().
Вектор (Vector)
vector - вид последовательности, которая поддерживает итераторы произвольного доступа. Кроме того, он поддерживает операции вставки и удаления в конце с постоянным (амортизированным) временем; вставка и удаление в середине занимают линейное время. Управление памятью обрабатывается автоматически, хотя для улучшения эффективности можно давать подсказки.
template class vector { public: // определения типов (typedefs): typedef iterator; typedef const_iterator; typedef Allocator::pointer pointer; typedef Allocator::reference reference; typedef Allocator::const_reference const_reference; typedef size_type; typedef difference_type; typedef T value_type; typedef reverse_iterator; typedef const_reverse_iterator; // размещение/освобождение (allocation/deallocation): vector(); vector(size_type n, const T& value = T()); vector(const vector& x); template vector(InputIterator first, InputIterator last); ~vector(); vector& operator=(const vector& x); void reserve(size_type n); void swap(vector& x); // средства доступа (accessors): iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin(); reverse_iterator rend(); const_reverse_iterator rend(); size_type size() const; size_type max_size() const; size_type capacity() const; bool empty() const; reference operator[](size_type n); const_reference operator[](size_type n) const; reference front(); const_reference front() const; reference back(); const_reference back() const; // вставка/стирание (insert/irase): void push_back(const T& x); iterator insert(iterator position, const T& x = T()); void insert(iterator position, size_type n, const T& x); template void insert(iterator position, InputIterator first, InputIterator last); void pop_back(); void erase(iterator position); void erase(iterator first, iterator last); }; template bool operator==(const vector& x, const vector& y); template bool operator& x, const vector& y); iterator - это итератор произвольного доступа, ссылающийся на T.
Точный тип зависит от исполнения и определяется в Allocator.
const_iterator - это постоянный итератор произвольного доступа, ссылающийся на const T. Точный тип зависит от исполнения и определяется в Allocator. Гарантируется, что имеется конструктор для const_iterator из iterator.
size_type - беззнаковый целочисленный тип. Точный тип зависит от исполнения и определяется в Allocator.
difference_type - знаковый целочисленный тип. Точный тип зависит от исполнения и определяется в Allocator.
Конструктор template
Функция-член capasity (ёмкость) возвращает размер распределённой памяти в векторе. Функция-член reserve - директива, которая сообщает vector(вектору) запланированноe изменение размера, так чтобы он мог соответственно управлять распределением памяти. Это не изменяет размер последовательности и занимает, самое большее, линейное время от размера последовательности. Перераспределение в этом случае происходит тогда и только тогда, когда текущая ёмкость меньше, чем параметр reserve. После reserve ёмкость (capasity) больше или равна параметру reserve, если происходит перераспределение; а иначе равна предыдущему значению capasity. Перераспределение делает недействительными все ссылки, указатели и итераторы, ссылающиеся на элементы в последовательности. Гарантируется, что нет никакого перераспределения во время вставок, которые происходят после того, как reserve выполняется, до времени, когда размер вектора достигает размера, указанного reserve.
insert (вставка) вызывает перераспределение, если новый размер больше, чем старая ёмкость. Если никакого перераспределения не происходит, все итераторы и ссылки перед точкой вставки остаются справедливыми. Вставка единственного элемента в вектор линейна относительно расстояния от точки вставки до конца вектора. Амортизированная сложность во время жизни вектора, вставляющего единственный элемент в свой конец, постоянна. Вставка множественных элементов в вектор с единственным вызовом вставляющей функции-члена линейна относительно суммы числа элементов плюс расстояние до конца вектора. Другими словами, намного быстрее вставить много элементов в середину вектора сразу, чем делать вставку по одному элементу. Шаблонная вставляющая функция-член предраспределяет достаточно памяти для вставки, если итераторы first и last относятся к последовательной, двунаправленной или произвольного доступа категориям. Иначе функция вставляет элементы один за другим и не должна использоваться для вставки в середину векторов.
erase (стирание) делает недействительными все итераторы и ссылки после пункта стирания. Деструктор T вызывается столько раз, каково число стёртых элементов, а оператор присваивания T вызывается столько раз, каково число элементов в векторе после стёртых элементов.
Чтобы оптимизировать распределение места, даётся определение для bool.
class vector { public: // битовая ссылка (bit reference): class reference { public: ~reference(); operator bool() const; reference& operator=(const bool x); void flip(); // инвертирует бит (flips the bit ) }; // определения типов (typedefs): typedef bool const_reference; typedef iterator; typedef const_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef bool value_type; typedef reverse_iterator; typedef const_reverse_iterator; // размещение/освобождение (allocation/deallocation): vector(); vector(size_type n, const bool& value = bool()); vector(const vector& x); template vector(InputIterator first, InputIterator last); ~vector(); vector& operator=(const vector& x); void reserve(size_type n); void swap(vector& x); // средства доступа (accessors): iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin(); reverse_iterator rend(); const_reverse_iterator rend(); size_type size() const; size_type max_size() const; size_type capacity() const; bool empty() const; reference operator[](size_type n); const_reference operator[](size_type n) const; reference front(); const_reference front() const; reference back(); const_reference back() const; // вставка/стирание (insert/irase): void push_back(const bool& x); iterator insert(iterator position, const bool& x = bool()); void insert(iterator position, size_type n, const bool& x); template void insert(iterator position, InputIterator first, InputIterator last); void pop_back(); void erase(iterator position); void erase(iterator first, iterator last); }; void swap(vector::reference x, vector::reference y); bool operator==(const vector& x, const vector& y); bool operator& x, const vector& y); reference - класс, который имитирует поведение ссылок отдельного бита в vector.
Ожидается, что каждое исполнение обеспечит определение vector для всех поддерживаемых моделей памяти.
| Сейчас невозможно шаблонизировать определение. То есть мы не можем написать: template class Allocator == allocator> class vector { /* ... */ }; Поэтому обеспечивается только vector. |
Итератор входного потока (Istream Iterator)
istream_iterator
Невозможно записывать что-либо с использованием входных итераторов. Основная особенность входных итераторов - тот факт, что операторы ++ не сохраняют равенства, то есть i == j не гарантирует вообще, что ++ i == ++ j. Каждый раз, когда ++ используется, читается новое значение. Практическое следствие этого факта - то, что входные итераторы могут использоваться только для однопроходных алгоритмов, что действительно имеет здравый смысл, так как многопроходным алгоритмам всегда более соответствует использование структур данных в оперативной памяти.
Два итератора конец-потока всегда равны. Итератор конец-потока не равен не-конец-потока итератору. Два не-конец-потока итератора равны, когда они созданы из того же самого потока.
template
Итератор выходного потока (Ostream Iterator)
istream_iterator
while (first != last) *result++ = *first++; ostream_iterator определён как:
template
ИТЕРАТОРЫ ПОТОКОВ
partial_sum_copy(istream_iterator
АЛГОРИТМЫ
Для некоторых алгоритмов предусмотрены и оперативные и копирующие версии. Решение, включать ли копирующую версию, было обычно основано на рассмотрении сложности. Когда стоимость выполнения операции доминирует над стоимостью копии, копирующая версия не включена. Например, sort_copy не включена, так как стоимость сортировки намного значительнее, и пользователи могли бы также делать copy перед sort. Когда такая версия предусмотрена для какого-то алгоритма algorithm, он называется algorithm_copy. Алгоритмы, которые берут предикаты, оканчиваются суффиксом _if (который следует за суффиксом _copy).
Класс Predicate используется всякий раз, когда алгоритм ожидает функциональный объект, при применении которого к результату разыменования соответствующего итератора возвращается значение, обратимое в bool. Другими словами, если алгоритм берёт Predicate pred как свой параметр и first как свой параметр итератора, он должен работать правильно в конструкции if (pred(*first)) {...}. Предполагается, что функциональный объект pred не применяет какую-либо непостоянную функцию для разыменованного итератора.
Класс BinaryPredicate используется всякий раз, когда алгоритм ожидает функциональный объект, который при его применении к результату разыменования двух соответствующих итераторов или к разыменованию итератора и типа T, когда T - часть сигнатуры, возвращает значение, обратимое в bool. Другими словами, если алгоритм берёт BinaryPredicate binary_pred как свой параметр и first1 и first2 как свои параметры итераторов, он должен работать правильно в конструкции if (binary_pred(*first, *first2)) {...}. BinaryPredicate всегда берёт тип первого итератора как свой первый параметр, то есть в тех случаях, когда T value - часть сигнатуры, он должен работать правильно в контексте if (binary_pred (*first, value)) {...}. Ожидается, что binary_pred не будет применять какую-либо непостоянную функцию для разыменованных итераторов.
В описании алгоритмов операторы + и - используются для некоторых категорий итераторов, для которых они не должны быть определены. В этих случаях семантика a+n такая же, как семантика {X tmp = a; advance(tmp, n); return tmp;}, а семантика a-b такая же, как семантика {Distance n; distance(a, b, n); return n;}.
Частичная сумма (Partial sum)
template
Двоичный поиск (Binary search)
Все алгоритмы в этом разделе - версии двоичного поиска. Они работают с итераторами не произвольного доступа, уменьшая число сравнений, которое будет логарифмическим для всех типов итераторов. Они особенно подходят для итераторов произвольного доступа, так как эти алгоритмы делают логарифмическое число шагов в структуре данных. Для итераторов не произвольного доступа они выполняют линейное число шагов.
template
template
template
template
Генераторы перестановок (Permutation generators)
template
template
Копировать (Copy)
template
template
Лексикографическое сравнение (Lexicographical comparison)
template
Минимум и максимум (Minimum and maximum)
template
template
template
N-й элемент (Nth element)
template
Найти (Find)
template
Найти рядом (Аdjacent find)
template
Накопление (Accumulate)
template
Объединение (Merge)
template
template
Обменять (Swap)
template
template
tempate
Операции над множеством для сортированных структур (Set operations on sorted structures)
Этот раздел определяет все основные операции над множеством для сортированных структур. Они даже работают с множествами с дубликатами, содержащими множественные копии равных элементов. Семантика операций над множеством обобщена на множества с дубликатами стандартным способом, определяя объединение, содержащее максимальное число местонахождений каждого элемента, пересечение, содержащее минимум, и так далее. template
template
template
Он возвращает конец созданного диапазона. Гарантируется, что set_intersection устойчив, то есть, если элемент присутствует в обоих диапазонах, он копируется из первого диапазона. Выполняется максимально ((last1 - first1) + (last2 - first2)) * 2 - 1 сравнений. Результат set_union не определён, если возникающий в результате диапазон перекрывается с любым из первоначальных диапазонов.
template
template
Операции над пирамидами (Heap operations)
Пирамида - специфическая организация элементов в диапазоне между двумя итераторами произвольного доступа [a, b). Два её ключевые свойства: (1) *a - самый большой элемент в диапазоне, (2) *a может быть удалён с помощью pop_heap или новый элемент добавлен с помощью push_heap за O(logN) время. Эти свойства делают пирамиды полезными для приоритетных очередей. make_heap преобразовывает диапазон в пирамиду, a sort_heap превращает пирамиду в сортированную последовательность.
template
template
template
template
Операции с каждым элементом (For each)
template
Операции сортировки и отношения (Sorting and related operations)
Все операции в этом разделе имеют две версии: одна берёт в качестве параметра функциональный объект типа Compare, а другая использует operator< .
Compare - функциональный объект, который возвращает значение, обратимое в bool. Compare comp используется полностью для алгоритмов, принимающих отношение упорядочения. comp удовлетворяет стандартным аксиомам для полного упорядочения и не применяет никакую непостоянную функцию к разыменованному итератору. Для всех алгоритмов, которые берут Compare, имеется версия, которая использует operator< взамен. То есть comp(*i, *j) == true по умолчанию для *i < *j == true.
Последовательность сортируется относительно компаратора comp, если для любого итератора i, указывающего на элемент в последовательности, и любого неотрицательного целого числе n такого, что i + n является допустимым итератором, указывающим на элемент той же самой последовательности, comp(*( i + n), *i) == false.
В описаниях функций, которые имеют дело с упорядочивающими отношениями, мы часто используем представление равенства, чтобы описать такие понятия, как устойчивость. Равенство, к которому мы обращаемся, не обязательно operator==, а отношение равенства стимулируется полным упорядочением. То есть два элементa a и b считаются равными, если и только если !(a < b) && !(b < a).
Отличие (Mismatch)
template
Переместить по кругу (Rotate)
template
template
Перетасовать (Random shuffle)
template
Подсчет (Count)
template
count должен сохранять результат в параметре ссылки вместо того, чтобы возвращать его, потому что тип размера не может быть выведен из встроенных типов итераторов, как, например, int*.
Поиск подпоследовательности (Search)
template
Породить (Generate)
template
Преобразовать (Transform)
template
Расположить в обратном порядке (Reverse)
template
template
Разделить (Partitions)
template
template
Скалярное произведение (Inner product)
template
Смежная разность (Adjacent difference)
template
Сортировка (Sort)
template
template
template
template
Сравнение на равенство (Equal)
template
Убрать повторы (Unique)
template
template
Удалить (Remove)
template
template
Заменить (Replace)
template
template
Заполнить (Fill)
template
Адаптеры функций (Function adaptors)
Функциональные адаптеры работают только с классами функциональных объектов с определёнными типами параметров и типом результата.
Адаптеры контейнеров (Container adaptors)
Часто бывает полезно обеспечить ограниченные интерфейсы контейнеров. Библиотека предоставляет stack, queue и priority_queue через адаптеры, которые могут работать с различными типами последовательностей.
Адаптеры указателей на функции (Adaptors for pointers to functions)
Чтобы позволить указателям на (унарные и бинарные) функции работать с функциональными адаптерами, библиотека обеспечивает следующее:
template
Системы трансляции, которые имеют множественный указатель на типы функций, должны обеспечить дополнительные шаблоны функций ptr_fun.
АДАПТЕРЫ
Итераторы вставки (Insert iterators)
Чтобы было возможно иметь дело с вставкой таким же образом, как с записью в массив, в библиотеке обеспечивается специальный вид адаптеров итераторов, называемых итераторами вставки (insert iterators). С обычными классами итераторов
while (first != last) *result++ = *first++;
вызывает копирование диапазона [first, last) в диапазон, начинающийся с result. Тот же самый код с result, являющимся итератором вставки, вставит соответствующие элементы в контейнер. Такой механизм позволяет всем алгоритмам копирования в библиотеке работать в режиме вставки (insert mode) вместо обычного режима наложения записей. Итератор вставки создаётся из контейнера и, возможно, одного из его итераторов, указывающих, где вставка происходит, если это ни в начале, ни в конце контейнера. Итераторы вставки удовлетворяют требованиям итераторов вывода. operator* возвращает непосредственно сам итератор вставки. Присваивание operator=(const T& х) определено для итераторов вставки, чтобы разрешить запись в них, оно вставляет х прямо перед позицией, куда итератор вставки указывает. Другими словами, итератор вставки подобен курсору, указывающему в контейнер, где происходит вставка. back_insert_iterator вставляет элементы в конце контейнера, front_insert_iterator вставляет элементы в начале контейнера, а insert_iterator вставляет элементы, куда итератор указывает в контейнере. back_inserter, front_inserter и inserter - три функции, создающие итераторы вставки из контейнера.
template
Обратные итераторы (Reverse iterators)
Двунаправленные итераторы и итераторы произвольного доступа имеют соответствующие адаптеры обратных итераторов, которые выполняют итерации через структуру данных в противоположном направлении.Они имеют те же самые сигнатуры, как и соответствующие итераторы. Фундаментальное соотношение между обратным итератором и его соответствующим итератором i установлено тождеством &*(reverse_iterator(i)) == &*(i - 1). Это отображение продиктовано тем, что, в то время как после конца массива всегда есть указатель, может не быть допустимого указателя перед началом массива.
template