Как использовать / создать boost :: multi_index?

Может кто-нибудь объяснить мне подробно, как создать многоиндексную карту, используя boost::multi_index? Я видел много примеров в Интернете, а также страницу поддержки, но я не мог этого понять. Я хотел бы отобразить указатель объекта класса на несколько int / long в качестве ключей. Может кто-нибудь, пожалуйста, помогите мне понять это?

У меня есть класс X и несколько свойств класса, которые long long, long, int, int, Я хочу хранить свойства long long, long, int, int в качестве ключей для сопоставления -> <указатель на X>,

Я хочу, чтобы иметь возможность искать указатель на любое свойство. Некоторые из свойств являются уникальными для каждого объекта X а некоторые не уникальны.

4

Решение

Boost.Multi индекс предлагает чрезвычайно настраиваемый интерфейс за счет предложения чрезвычайно сложного интерфейса, поэтому легко понять, почему вы застряли.

Я представлю прокомментированный пример, который должен соответствовать вашему варианту использования.

Во-первых, наши данные:

struct X
{
long long l; // assume unique
int i1; // assume unique
int i2; // assume non-unique
// plus any ohter data you have in your class X
};

Далее мы подготовим один тег для каждого индекса, который мы хотим, чтобы контейнер имел. Теги не являются строго необходимыми (индексы могут быть доступны по их порядку), но более удобно указывать имя для каждого индекса:

struct IndexByL {};
struct IndexByI1 {};
struct IndexByI2 {};

Теперь у нас есть то, что нам нужно, чтобы собрать тип контейнера:

using Container = boost::multi_index_container<
X*, // the data type stored
boost::multi_index::indexed_by< // list of indexes
boost::multi_index::hashed_unique<  //hashed index over 'l'
boost::multi_index::tag<IndexByL>, // give that index a name
boost::multi_index::member<X, long long, &X::l> // what will be the index's key
>,
boost::multi_index::ordered_unique<  //ordered index over 'i1'
boost::multi_index::tag<IndexByI1>, // give that index a name
boost::multi_index::member<X, int, &X::i1> // what will be the index's key
>,
boost::multi_index::hashed_non_unique<  //hashed non-unique index over 'i2'
boost::multi_index::tag<IndexByI2>, // give that index a name
boost::multi_index::member<X, int, &X::i2> // what will be the index's key
>
>
>;

Вот и все, у нас есть контейнер. Далее, вот как мы можем использовать это:

Container c;  // empty container
X x1{...}, x2{...}, x3{...};  // some data

// Insert some elements
auto& indexByL = c.get<IndexByL>(); // here's where index tags (=names) come in handy
indexByL.insert(&x1);
indexByL.insert(&x2);
indexByL.insert(&x3);

// Look up by i1
auto& indexByI1 = c.get<IndexByI1>();
auto itFound = indexByI1.find(42);
if (itFound != indexByI1.end())
{
X *x = *itFound;
}

// Look up by i2
auto& indexByI2 = c.get<IndexByI2>();
size_t numberOfHundreds = indexByI2.count(100);

[Живой пример]

А теперь немного прозы о том, как зверь работает вообще.

Вы определяете многоиндексный контейнер, указывая тип объектов, которые он будет хранить (X* в вашем случае) и один или несколько индексов, которые можно использовать для доступа к хранимым объектам. Думайте об индексах как об интерфейсах для доступа к данным.

Индексы могут быть разных видов:

  • Индексы на основе упорядочения по ключу (думаю, std::set или же std::map)
  • Индексы основаны на ранжировании по ключу (так же, плюс легкий доступ к Nй элемент)
  • Индексы, основанные на хешировании ключа (подумайте std::unordered_set или же std::unordered_map)
  • Индексы на основе доступа в стабильном порядке (думаю, std::list)
  • Индексы на основе произвольного доступа в стабильном порядке (думаю, std::vector)

Индексы на основе ключей также могут быть уникальными (например, std::map) или неуникально (например, std::multimap).

При определении контейнера вы передаете каждый индекс, который хотите иметь, в качестве одного аргумента шаблона boost::multi_index::indexed_by, (В нашем примере выше я добавил три индекса).

Для индексов, которые не используют ключ (стабильный порядок & произвольный доступ), ничего не нужно указывать; Вы просто говорите: «Я хочу такой индекс».

Для индексов на основе ключей также необходимо указать, как ключ получается из данных. Вот где ключевые экстракторы вступают в игру. (Это три варианта использования boost::multi_index::member в примере). По сути, для каждого индекса вы предоставляете рецепт (или алгоритм) для получения ключа из данных, хранящихся в контейнере. В настоящее время доступны ключевые экстракторы:

  • Используйте сам элемент: identity
  • Используйте элемент данных элемента: member
  • Используйте (постоянную) функцию-член элемента: [const_]mem_fun
  • Используйте глобальную функцию: global_fun
  • Объедините несколько ключевых экстракторов в один: composite_key

Обратите внимание, что экстракторы ключей могут прозрачно разыменовывать указатели. То есть, если ваш элемент данных является указателем на класс CВы можете указать ключевые экстракторы над классом C и разыменование произойдет автоматически. (Это свойство также используется в примере).

Таким образом определяется контейнер с индексами. Чтобы получить доступ к индексу, вы вызываете get шаблон функции-члена на контейнере. Вы можете ссылаться на индекс по его порядковому номеру в списке аргументов шаблона indexed_by, Однако для более удобочитаемой манипуляции вы можете ввести тег для каждого индекса (или только некоторые из них). Тег — это произвольный тип (обычно пустая структура с подходящим именем), который позволяет использовать этот тип в качестве аргумента шаблона для get вместо порядкового номера индекса. (Это также используется в примере).

Получив ссылку на индекс из контейнера, вы можете использовать его так же, как структуру данных, которой соответствует индекс (map, hashset, vector, …). Изменения, сделанные с помощью этого индекса, затронут весь контейнер.

9

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]