STL-наборы и мультинаборы представляют собой контейнеры различной длины, в которых элементы эффективно сортируются в соответствии с заданным сравнительным предикатом. Эти классы контейнеров не имеют функциональности, когда программист хочет эффективно сортировать и искать элементы по другому критерию сортировки. Рассмотрим, например:
Тот факт, что идентификаторы уникальны для каждого сотрудника, отражается в определении<operator<>, поэтому естественная структура данных для хранения<employee>s является просто<std::set<employee>>. Теперь, если вы хотите распечатать список всех сотрудников в алфавитном порядке, доступные решения могут иметь недостатки с точки зрения пространства для хранения, сложности или простоты обслуживания:
Копируйте работника, вставленного в вектор или аналогичный, и сортируйте это по функторам сравнения, зависящим от<employee::name>.
Сохраните вторичную структуру данных указателей на элементы набора, соответствующим образом отсортированные по имени.
Из них, вероятно, вторым решением будет решение, принятое большинством программистов, обеспокоенных эффективностью, но оно налагает раздражающее бремя сохранения синхронизации двух структур. Если дополнительно требуется, чтобы код был безопасным для исключений, эта конструкция легко становится неподъемной.
Boost.MultiIndex имеетупорядоченные индексы, которые сортируют элементы в соответствии с конкретным ключом и предназначены для помощи программистам, нуждающимся в последовательностях элементов, для которыхболее одногокритерии сортировки актуальны. Мы делаем это, определяя<multi_index_container>инстанциацию, состоящую из нескольких упорядоченных индексов: каждый индекс, рассматриваемый изолированно, ведет себя во многом как упорядоченный<std::set>(или<std::multiset>), при этом сохраняется общая целостность всей структуры данных. Таким образом, нашу проблему можно решить с помощью Boost. MultiIndex выглядит следующим образом:
#include<boost/multi_index_container.hpp>#include<boost/multi_index/ordered_index.hpp>#include<boost/multi_index/identity.hpp>#include<boost/multi_index/member.hpp>// define a multiply indexed set with indices by id and nametypedefmulti_index_container<employee,indexed_by<// sort by employee::operator<ordered_unique<identity<employee>>,// sort by less<string> on nameordered_non_unique<member<employee,std::string,&employee::name>>>>employee_set;voidprint_out_by_name(constemployee_set&es){// get a view to index #1 (name)constemployee_set::nth_index<1>::type&name_index=es.get<1>();// use name_index as a regular std::setstd::copy(name_index.begin(),name_index.end(),std::ostream_iterator<employee>(std::cout));}
Вместо одного типа сравнительных предикатов, как это происходит для ассоциативных контейнеров STL,<multi_index_container>пропускаетсясписокспецификаций индекса<indexed_by>, каждый из которых индуцирует соответствующий индекс. Доступ к индексам осуществляется через<get><<N>()>, гдеNколеблется между 0 и числом сравнительных предикатов минус один. Доступ к функциональности индекса #0 можно получить непосредственно с объекта<multi_index_container>без использования<get<0>()>: например,<es.begin()>эквивалентно<es.get<0>().begin()>.
Обратите внимание, что<get>возвращаетссылкуна индекс, а не на объект индекса. Индексы не могут быть построены как отдельные объекты из контейнера, к которому они принадлежат.
// Wrong: we forgot the & after employee_set::nth_index<1>::typeconstemployee_set::nth_index<1>::typename_index=es.get<1>();
не компилирует, так как пытается построить индексный объект<name_index>. Это общий источник ошибок в коде пользователя.
Этот случай исследования позволяет нам ввести так называемыесеквенированные индексы, и как они взаимодействуют с упорядоченными индексами для построения мощных контейнеров. Предположим, что у нас есть текст, разбитый на слова и сохраненный в таком списке:
typedefstd::list<std::string>text_container;std::stringtext="Alice was beginning to get very tired of sitting by her sister on the ""bank, and of having nothing to do: once or twice she had peeped into the ""book her sister was reading, but it had no pictures or conversations in ""it, 'and what is the use of a book,' thought Alice 'without pictures or ""conversation?'";// feed the text into the listtext_containertc;boost::tokenizer<boost::char_separator<char>>tok(text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));std::copy(tok.begin(),tok.end(),std::back_inserter(tc));
Если мы хотим подсчитать количество слов в тексте, мы прибегнем к<std::count>:
Но эта реализация<occurrences>выполняется в линейном времени, что может быть неприемлемо для большого количества текста. Аналогично, другие операции, такие как удаление выбранных слов, слишком дороги для выполнения<std::list>:
voiddelete_word(conststd::string&word){tc.remove(word);// scans the entire list looking for word}
Когда производительность вызывает беспокойство, нам понадобится дополнительная структура данных, которая индексирует элементы в<tc>, предположительно в алфавитном порядке. Повышаю. MultiIndex делает это с помощью комбинации секвенированных и упорядоченных индексов:
#include<boost/multi_index_container.hpp>#include<boost/multi_index/sequenced_index.hpp>#include<boost/multi_index/ordered_index.hpp>#include<boost/multi_index/identity.hpp>// define a multi_index_container with a list-like index and an ordered indextypedefmulti_index_container<std::string,indexed_by<sequenced<>,// list-like indexordered_non_unique<identity<std::string>>// words by alphabetical order>>text_container;std::stringtext=...// feed the text into the listtext_containertc;boost::tokenizer<boost::char_separator<char>>tok(text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));std::copy(tok.begin(),tok.end(),std::back_inserter(tc));
Пока что замена<multi_index_container>на<std::list>не показывает никакого преимущества. Код для вставки текста в контейнер не изменяется, поскольку секвенированные индексы обеспечивают интерфейс, аналогичный интерфейсу<std::list>(не требуется явного доступа к этому индексу через<get<0>()>, поскольку<multi_index_container>наследует функциональность индекса #0). Но спецификация дополнительного упорядоченного индекса позволяет реализовать<occurrences>и<delete_word>гораздо более эффективно:
std::size_toccurrences(conststd::string&word){// get a view to index #1text_container::nth_index<1>::type&sorted_index=tc.get<1>();// use sorted_index as a regular std::setreturnsorted_index.count(word);}voiddelete_word(conststd::string&word){// get a view to index #1text_container::nth_index<1>::type&sorted_index=tc.get<1>();// use sorted_index as a regular std::setsorted_index.erase(word);}
<occurrences>и<delete_word>имеют логарифмическую сложность. Программист может использовать индекс #0 для доступа к тексту, как с<std::list>, и использовать индекс #1, когда требуется логарифмический поиск.
Мы указываем два индекса, первый изсеквенированного типа, второй неуникальныйупорядоченный индекс. В общем, можно указать произвольное число индексов: каждый из аргументов<indexed_by>называетсяуказателем индекса. В зависимости от типа указанного индекса соответствующему спецификатору потребуется дополнительная информация: например, спецификаторы<ordered_unique>и<ordered_non_unique>снабженыключевым экстрактороми необязательнымсравнительным предикатом, которые совместно указывают, как будет выполняться сортировка элементов.
Инстанциация<multi_index_container>может быть объявлена без подачи части<indexed_by>: в этом случае значения индекса по умолчанию принимаются так, что полученный тип эквивалентен обычному<std::set>. Конкретно, инстанциация
Для того, чтобы получить (ссылку) индекс данного<multi_index_container>, программист должен предоставить свой номер заказа, который является громоздким и не очень самоописательным. Необязательно индексы могут быть назначенытеги(типы C++), которые действуют как более удобная мнемоника. При условии, что метки должны быть пропущены в качестве первого параметра соответствующего указателя индекса. Ниже приводится пересмотренная версия<employee_set>с включением тегов:
Для этого необходимо, чтобы они находились в<tag>. Любой тип может быть использован в качестве тега для индекса, хотя в целом выберите имена, которые являются описательными для индекса, с которым они связаны. Механизм тегов позволяет нам писать такие выражения, как:
Если для индекса не предусмотрена тег (как в случае с индексом #0 предыдущего примера), доступ к этому индексу может быть выполнен только по номеру. Обратите внимание на существование двух различных<typedef>s<nth_index>и<index>для обозначения индекса по номеру и по тегу, соответственно; например,
< [50] >[Thoppa,]< [51] >[Medcoin] (один из них —).
<get()>, с другой стороны, перегружен для обслуживания обоих стилей доступа:employee_set::nth_index<1>::type- тип индекса #1,
employee_set::index<name>::type— тип индекса, помеченногоname(в данном случае тот же индекс #1).
get(), on the other hand, is overloaded to serve both styles of access:
[ORIG_END] -->
employee_set::index<name>::type&name_index=es.get<name>();employee_set::nth_index<1>::type&name_index2=es.get<1>();// same index
Кроме того, шаблон класса<tag>принимает несколько тегов для одного индекса, которые мы можем использовать взаимозаменяемо: например, спецификация индекса #1 в предыдущем примере может быть переписана для хранения двух разных тегов<name>и<by_name>:
Каждый индекс<multi_index_container>использует свои собственные типы итераторов, которые отличаются от других индексов. Как правило, в контейнерах STL эти итераторы определяются как вложенные типы индекса:
Итераторы, предоставляемые каждым индексом, являютсяпостоянными, то есть элементы, на которые они указывают, не могут быть мутированы непосредственно. Это следует за интерфейсом<std::set>для упорядоченных индексов, но может стать сюрпризом для других типов, таких как секвенированные индексы, которые моделируются после<std::list>, где это ограничение не происходит. Это, казалось бы, странное поведение навязывается тем, как работает<multi_index_container>; если бы элементы можно было без разбора мутировать, мы могли бы ввести несоответствия в упорядоченные индексы<multi_index_container>без уведомления контейнера об этом. Модификация элементов производится с помощью операций обновленияна любом индексе.
Интерфейс интерфейса, интерфейса интерфейса кеквенирования мессенджеров, а также адаптационной ленты и позиционирования.
Примеры во введенииосуществляют упорядоченные и секвенированные индексы, которые наиболее часто используются; другие виды индексов представлены в разделе учебникатипов индексов.Упорядоченные индексы сортируют такие элементы, какstd::set, и обеспечивают аналогичный интерфейс. Естьуникальныеинеуникальныеварианты: первые не допускают дубликатов, в то время как последние разрешают их (например,std::multiset).
Ранжированные индексы - это вариация упорядоченных индексов, обеспечивающая дополнительные возможности для запроса и доступа к элементам на основе их ранга(численное положение, которое они занимают в индексе).
Последовательные индексы моделируются по семантике и интерфейсуstd::list: Они располагают элементы как бы в двунаправленном списке.
Хешированные индексы обеспечивают быстрый доступ к элементам с помощью методов хеширования, аналогично нестандартнымhash_set, предоставляемым некоторыми поставщиками. Недавнонеупорядоченные ассоциативные контейнерыбыли предложены в рамках расширения стандартной библиотеки C++, известной в компиее стандартизации как TR1. Индексы хеширования тщательно моделируют это предложение.
Индексы случайного доступа обеспечивают интерфейс, аналогичный интерфейсу секвенированных индексов, а также имеют итераторы случайного доступа и позиционный доступ к элементам.
The examples in the introduction exercise ordered and sequenced
indices, which are the most commonly used; the other kinds of indices are presented
in the index types section of the tutorial.
[ORIG_END] -->
Упорядоченные индексы сортируют элементы в<multi_index_container>в соответствии с заданным ключом и связанным с ним сравнительным предикатом. Эти индексы можно рассматривать как аналоги стандартного контейнера<std::set>, и на самом деле они воспроизводят его интерфейс, хотя и с некоторыми незначительными различиями, продиктованными общими ограничениями Boost. MultiIndex.
Упорядоченные индексы классифицируются науникальные, которые запрещают двум элементам иметь одинаковое ключевое значение, инеуникальныеиндексы, которые допускают дубликаты. Еще раз рассмотрим определение
В этом случае<multi_index_container>первый индекс должен рассматриваться как уникальный (поскольку идентификаторы являются эксклюзивными для каждого сотрудника) и, таким образом, объявляется с использованием<ordered_unique>, тогда как второй индекс не является уникальным (поскольку существует возможность, что два Джона Смита наняты в одной и той же компании), что определяется использованием<ordered_non_unique>.
Классификация упорядоченных индексов в уникальном и неуникальном оказывает влияние на то, какие элементы разрешено вставлять в данный<multi_index_container>; кратко говоря, уникальные упорядоченные индексы имитируют поведение<std::set>с, в то время как неуникальные упорядоченные индексы аналогичны<std::multiset>с. Например,<employee_set>может удерживать объекты<employee(0,"George Brown")>и<employee(1,"George Brown")>, но не принимает вставку<employee>объекта, идентификатор которого совпадает с идентификатором некоторого ранее вставленного сотрудника.
Можно указать более одного уникального индекса. Например, если мы добавим<employee>, чтобы включить дополнительный член для номера социального страхования, который разумно рассматривается как уникальный, следующее улавливает этот дизайн:
structemployee{intid;std::stringname;intssnumber;employee(intid,conststd::string&name,intssnumber):id(id),name(name),ssnumber(ssnumber){}booloperator<(constemployee&e)const{returnid<e.id;}};typedefmulti_index_container<employee,indexed_by<// sort by employee::operator<ordered_unique<identity<employee>>,// sort by less<string> on nameordered_non_unique<member<employee,std::string,&employee::name>>,// sort by less<int> on ssnumberordered_unique<member<employee,int,&employee::ssnumber>>>>employee_set;
Первый необязательный аргумент используется, еслиметкисвязаны с индексом. Теперь перейдем к краткому обсуждению оставшихся аргументов упорядоченного указателя индекса.
Первый параметр шаблона (или второй, если теги поставляются) в спецификации упорядоченного индекса обеспечиваетизвлечение ключапредиката. Этот предикат берет целый элемент (в нашем примере ссылку на<employee>объект) и возвращает часть информации, с помощью которой выполняется сортировка. В большинстве случаев возникает одна из следующих двух ситуаций:
Сравнительно с тем, что он находится на грани, а также с тем, что он находится на грани банкротства. Boost.MultiIndex exte< [93] >, , , , , , , , .
В качестве примера рассмотрим определение<employee_set>. Определение первого индекса:Ключом служит весь элемент, как и в случае первого индексаemployee_set. Предопределенныйidentityпредикат может быть использован здесь в качестве ключевого экстрактора;identityвозвращает в качестве ключа тот же объект, переданный в качестве аргумента.
Сравнение выполняется на конкретном элементе данных; это тесно связано со спецификацией индексов в столбце таблицы в реляционных базах данных. Boost.MultiIndex предоставляетmember, который возвращает в качестве ключа элемент, указанный заданным указателем.
As an example, consider again the definition of employee_set. The
definition of the first index:
[ORIG_END] -->
ordered_unique<identity<employee>>
С помощью<identity>указывается, что<element>сами объекты служат ключом к этому индексу. С другой стороны, во втором индексе:
Мы используем<member>для извлечения<name>части<employee>объекта. Ключевым типом индекса является<std::string>.
Помимо<identity>и<member>, Boost.MultiIndex предоставляет несколько других предопределенных ключевых экстракторов и мощные способы их объединения. Ключевые экстракторы также могут быть определены пользователем. Проконсультируйтесь с ключевым разделомизвлеченияучебника для более подробного изложения этой темы.
Последней частью спецификации упорядоченного индекса является ассоциированныйсравнительный предикат, который должен заказать ключи менее чем по моде. Эти сравнительные предикаты не отличаются от тех, которые используются контейнерами STL, такими как<std::set>. По умолчанию (т.е. при отсутствии сравнительного предиката) индекс с ключами типа<key_type>сортирует элементы по<std::less<key_type>>. Если необходимы другие критерии сравнения, они могут быть указаны в качестве дополнительного параметра в индексной декларации:
// define a multiply indexed set with indices by id and by name
// in reverse alphabetical ordertypedefmulti_index_container<employee,indexed_by<ordered_unique<identity<employee>>,// as usualordered_non_unique<member<employee,std::string,&employee::name>,std::greater<std::string>// default would be std::less<std::string>>>>employee_set;
Указанный упорядоченный индекс позволяет искать в зависимости от его ключевого типа, а не всего элемента. Например, чтобы найти Веронику Круз в<employee_set>, нужно написать:
В качестве плюса, Буст. MultiIndex предоставляет операции поиска, принимающие ключи поиска, отличные от<key_type>индекса, который является особенно полезным объектом, когда<key_type>объекты дороги для создания. Заказанные контейнеры STL не обеспечивают эту функциональность, что часто приводит к неэлегантным обходным путям: рассмотрим, например, проблему определения сотрудников, чьи идентификаторы попадают в диапазон [0,100]. Учитывая, что ключом<employee_set>индекса #0 является сам<employee>, при первом подходе можно было бы написать следующее:
Обратите внимание, однако, что<std::less<employee>>на самом деле зависит только от идентификаторов сотрудников, поэтому было бы удобнее избегать создания целых<employee>объектов только ради их идентификаторов. Повышаю. MultiIndex позволяет: определить подходящий предикат сравнения
structcomp_id{// compare an ID and an employeebooloperator()(intx,constemployee&e2)const{returnx<e2.id;}// compare an employee and an IDbooloperator()(constemployee&e1,intx)const{returne1.id<x;}};
Здесь мы не только передаем идентификаторы вместо<employee>объектов: передается и альтернативный сравнительный предикат. В целом операции поиска упорядоченных индексов перегружены для принятиясовместимых критериев сортировки. Определение совместимости в этом контексте дано в ссылке, но грубо говоря, мы говорим, что предикат сравнения<C1>совместим с<C2>, если любая последовательность, отсортированная по<C2>, также отсортирована по отношению к<C1>. Ниже показано более интересное использование совместимых предикатов:
// sorting by name's initialstructcomp_initial{booloperator()(charch,conststd::string&s)const{if(s.empty())returnfalse;returnch<s[0];}booloperator()(conststd::string&s,charch)const{if(s.empty())returntrue;returns[0]<ch;}};// obtain first employee whose name begins with 'J' (ordered by name)typedefemployee_set::index<name>::typeemployee_set_by_name;employee_set_by_name&name_index=es.get<name>();employee_set_by_name::const_iteratorit=name_index.lower_bound('J',comp_initial());
Поиск диапазона, т.е. поиск всех элементов в заданном интервале, является очень частой операцией, к которой можно прибегнуть стандартом<lower_bound>и<upper_bound>. Например, следующий код извлекает элементы<multi_index_container<double>>в интервале [100,200]:
typedefmulti_index_container<double>double_set;// note: default template parameters resolve to
// multi_index_container<double,indexed_by<unique<identity<double> > > >.double_sets;...double_set::iteratorit0=s.lower_bound(100.0);double_set::iteratorit1=s.upper_bound(200.0);// range [it0,it1) contains the elements in [100,200]
Тонкие изменения в коде необходимы при рассмотрении строгих неравенств. Для извлечения элементовбольше, чем 100 именьше, чем 200, код должен быть переписан как
double_set::iteratorit0=s.upper_bound(100.0);double_set::iteratorit1=s.lower_bound(200.0);// range [it0,it1) contains the elements in (100,200)
Чтобы добавить к этой сложности, осторожный программист должен учитывать, что нижняя и верхняя границы искомого интервала совместимы: например, если нижняя граница 200 и верхняя граница 100, итераторы<it0>и<it1>, произведенные кодом выше, будут в обратном порядке, с возможно катастрофическими результатами, если будет испытан переход от<it0>к<it1>. Все эти детали делают поиск диапазона утомительной и подверженной ошибкам задачей.
Функция члена<range>, часто в сочетании с выражениямиBoost.Lambda, может значительно помочь облегчить эту ситуацию:
usingnamespaceboost::lambda;typedefmulti_index_container<double>double_set;double_sets;...std::pair<double_set::iterator,double_set::iterator>p=s.range(100.0<=_1,_1<=200);// 100<= x <=200...p=s.range(100.0<_1,_1<200);// 100< x < 200...p=s.range(100.0<=_1,_1<200);// 100<= x < 200
<range>просто принимает предикаты, указывающие нижнюю и верхнюю границы искомого интервала. Пожалуйста, обратитесь к ссылке для подробного объяснения допустимых предикатов, переданных<range>.
Одна или обе границы могут быть опущены специальным маркером<unbounded>:
p=s.range(100.0<=_1,unbounded);// 100 <= xp=s.range(unbounded,_1<200.0);// x < 200p=s.range(unbounded,unbounded);// equiv. to std::make_pair(s.begin(),s.end())
Функция<replace>элемента выполняет замену данного элемента на месте, как показано в следующем примере:
typedefindex<employee_set,name>::typeemployee_set_by_name;employee_set_by_name&name_index=es.get<name>();employee_set_by_name::iteratorit=name_index.find("Anna Jones");employeeanna=*it;anna.name="Anna Smith";// she just got married to Calvin Smithname_index.replace(it,anna);// update her record
<replace>выполняет эту замену таким образом, что:
Сложность — это постоянное время, если изменённый элемент сохраняет свой первоначальный порядок относительно всех индексов; в противном случае он логарифмический.
Итератор и эталонная валидность сохраняются.
Операция полностью безопасна для исключения, т.е.<multi_index_container>остается неизменной, если выкинуто какое-либо исключение (происходящее от системы или типов данных пользователя).
<replace>- это мощная операция, не предусмотренная стандартными контейнерами STL, и особенно полезная, когда требуется сильная защита от исключений.
Наблюдательный читатель мог заметить, что удобство<replace>стоит дорого: а именно, весь элемент должен быть скопировандважды, чтобы сделать обновление (при извлечении его и внутри<replace>). Если элементы дороги для копирования, это может быть довольно вычислительной стоимостью для модификации только крошечной части объекта. Чтобы справиться с этой ситуацией, Бут. MultiIndex предоставляет альтернативный механизм обновления, называемый<modify>:
<modify>принимает функтор (или указатель на функцию), который передается со ссылкой на изменяемый элемент, тем самым устраняя необходимость в ложных копиях. Как и<replace>,<modify>сохраняет внутренние упорядочения всех индексов<multi_index_container>. Однако семантика<modify>не полностью эквивалентна<replace>. Рассмотрим, что происходит, если столкновение происходит в результате модификации элемента, то есть модифицированный элемент сталкивается с другим по отношению к некоторому уникальному упорядоченному индексу. В случае<replace>первоначальное значение сохраняется, и способ возвращается без изменения контейнера, но<modify>не может позволить себе такой подход, поскольку модифицирующий функтор не оставляет следов предыдущего значения элемента. Таким образом, ограничения целостности приводят к следующей политике: когда в процессе вызова происходит столкновение<modify>, элемент стирается и метод возвращается<false>. Существует еще одна версия<modify>, которая принимаетоткатфунктора, чтобы отменить изменения в случае столкновения:
structchange_id{change_id(intnew_id):new_id(new_id){}voidoperator()(employee&e){e.id=new_id;}private:intnew_id;};...employee_set::iteratorit=...intold_id=it->id;// keep the original id
// try to modify the id, restore it in case of collisionses.modify(it,change_id(321),change_id(old_id));
В примере<change_id(old_id)>используется для восстановления исходных условий, когда модификация приводит к столкновениям с каким-либо другим элементом. Различия в поведении между<replace>,<modify>и<modify>с откатом должны рассматриваться программистом на индивидуальной основе для определения наилучшего механизма обновления.
Behavior of the different updating mechanisms.
updating function
If there is a collision...
replace(it,x)
Замена не производится.
modify(it,mod)
Элемент стирается.
modify(it,mod,back)
<back>используется для восстановления исходных условий. (Если<back>бросает, элемент стирается.)
Также представлены версии<modify>, названные<modify_key>. В этом случае модифицирующие функторы передают ссылку на<key_type>часть элемента вместо всего объекта.
structchange_str{change_str(conststd::string&new_str):new_str(new_str){}// note this is passed a string, not an employeevoidoperator()(std::string&str){str=new_str;}private:std::stringnew_str;};...typedefemployee_set::index<name>::typeemployee_set_by_name;employee_set_by_name&name_index=es.get<name>();employee_set_by_name::iteratorit=name_index.find("Anna Jones");name_index.modify_key(it,change_str("Anna Smith"));
Как и<modify>, есть версии<modify_key>с откатом и без него.<modify>и<modify_key>особенно хорошо подходят для использования совместно сBoost.Lambdaдля определения модифицирующих функторов:
В отличие от упорядоченных индексов, секвенированные индексы не накладывают на элементы фиксированный порядок: вместо этого они могут быть расположены в любом положении на последовательности, так же, как это позволяет<std::list>. Интерфейс секвенированных индексов, таким образом, разработан на основе<std::list>; почти каждая операция, представленная в стандартном контейнере, воспроизводится здесь, иногда с изменениями синтаксиса и/или семантики, чтобы справиться с ограничениями, налагаемыми Boost. MultiIndex. Важным отличием, комментируемымвыше, является тот факт, что значения, на которые указывают итераторы секвенированных индексов, рассматриваются какконстанта:
multi_index_container<int,indexed_by<sequenced<>>>s;// list-like containers.push_front(0);*(s.begin())=1;// ERROR: the element cannot be changed
Как и в случае с любым другим типом индекса, модификация элементов может быть выполнена посредством операций обновления.
Рассмотрим<multi_index_container>с двумя или более индексами, один из которых секвенированного типа. Если элемент вставляется через другой индекс, он автоматически добавляется к концу последовательности индекса. Пример поможет прояснить это:
multi_index_container<int,indexed_by<sequenced<>,// sequenced typeordered_unique<identity<int>>// another index>>s;s.get<1>().insert(1);// insert 1 through index #1s.get<1>().insert(0);// insert 0 through index #1
// list elements through sequenced index #0std::copy(s.begin(),s.end(),std::ostream_iterator<int>(std::cout));// result: 1 0
Таким образом, поведение секвенированных индексов, когда вставки не производятся через них, заключается в сохранении порядка вставки.
Как упоминалось ранее, секвенированные индексы имитируют интерфейс<std::list>, и большинство исходных операций в нем также предоставляются. Однако семантика и сложность этих операций не всегда совпадают с операциями стандартного контейнера. Различия возникают главным образом из-за того, что вставки в секвенированный индекс не гарантируют успеха из-за возможного запрета другими индексами<multi_index_container>. Проконсультируйтесь сссылкойдля получения дополнительной информации.
Как и упорядоченные индексы, секвенированные индексы обеспечивают<replace>и.<modify>операций с идентичными функциональными возможностями. Однако аналога<modify_key>нет, поскольку секвенированные индексы не основаны на ключах.
Учитывая индексы<i1>и<i2>на одном и том же<multi_index_container>,<project>можно использовать для извлечения<i2>-итератора из<i1>-итератора, оба из которых указывают на один и тот же элемент контейнера. Эта функциональность позволяет программисту перемещаться между различными индексами одного и того же<multi_index_container>при выполнении сложных операций:
typedefemployee_set::index<name>::typeemployee_set_by_name;employee_set_by_name&name_index=es.get<name>();// list employees by ID starting from Robert Brown's IDemployee_set_by_name::iteratorit1=name_index.find("Robert Brown");// obtain an iterator of index #0 from it1employee_set::iteratorit2=es.project<0>(it1);std::copy(it2,es.end(),std::ostream_iterator<employee>(std::cout));
Немного более интересный пример:
text_containertc;// get a view to index #1 (ordered index on the words)text_container::nth_index<1>::type&sorted_index=tc.get<1>();// prepend "older" to all occurrences of "sister"text_container::nth_index<1>::type::iteratorit1=sorted_index.lower_bound("sister");while(it1!=sorted_index.end()&&*it1=="sister"){// convert to an iterator to the sequenced indextext_container::iteratorit2=tc.project<0>(it1);tc.insert(it2,"older");++it1;}
При наличии<project>можно также использоватьметки.
<multi_index_container>обеспечивает ту же сложность и гарантии безопасности, что и эквивалентные контейнеры STL. Итератор и эталонная валидность сохраняются перед вставками даже для замены и изменения операций.
Соответствующие инстанциации<multi_index_container>могут фактически имитировать<std::set>,<std::multiset>и (с большими ограничениями)<std::list>, как показано в разделеметодов. Эти модели столь же эффективны, как и оригинальные контейнеры STL; обратитесь к ссылкедля получения дополнительной информации о гарантиях сложности и разделупроизводительностидля практических измерений эффективности.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.