boost multi index

Boost.MultiIndex makes it possible to define containers that support an arbitrary number of interfaces. While std::vector provides an interface that supports direct access to elements with an index and std::set provides an interface that sorts elements. Boost.MultiIndex lets you definde containers that support both interfaces. Such a container could be used to access elements using an index and in a sorted fashion.

1.

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <string>
#include <iostream>

using namespace boost::multi_index;

struct animal {
  std::string name;
  int legs;
};

typedef multi_index_container<
  animal,
  indexed_by<
    hashed_non_unique<
      member<
       animal, std::string, &animal::name
      >
    >,
    hashed_non_unique<
      member<
        animal, int, &animal::legs
      >
    >
  >
> animal_multi;

int main() {
  animal_multi animals;

  animals.insert({"cat", 4});
  animals.insert({"shark", 0});
  animals.insert({"spider", 8});

  std::cout << animals.count("cat") << std::endl;
  
  const animal_multi::nth_index<1>::type& legs_index = animals.get<1>();
  std::cout << legs_index.count(8) << std::endl;
  return 0;
}

When you use Boost.MultiIndex, the first step is to define a new container. You have to decide which interfaces your new container should support and which element properties it should access.

multi_index_container is a class template that requires at least two parameters. The first parameter is the type of elements the container should store. The second parameter is used to denote different indexes the container should provide.

The key advantage of containers based on Boost.MultiIndex is that you can access elements via different interfaces. When you define a new container, you can specify the number and type of interfaces.

The advantage of the container animal_multi over a map like std::unordered_map is that animals can be looked up by name or by number of legs. animal_multi supports two interfaces, one based on the name and one based on the number of legs. The interface determines which member variable is the key and which member variable is the value. Because data such as names and legs can be keys of the MultiIndex container, the cannot be arbitrarily changed. If the number of legs is changed after an animal hase been looked up by name, an interface using legs as a key would be unaware of the change and would not know that a new hash value needs to be calculted.

2. boost::multi_index::hashed_unique

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <string>
#include <iostream>

using namespace boost::multi_index;

struct animal {
  std::string name;
  int legs;
};

typedef multi_index_container<
  animal,
  indexed_by<
    hashed_non_unique<
      member<
       animal, std::string, &animal::name
      >
    >,
    hashed_unique<
      member<
        animal, int, &animal::legs
      >
    >
  >
> animal_multi;

int main() {
  animal_multi animals;

  animals.insert({"cat", 4});
  animals.insert({"shark", 0});
  animals.insert({"dog", 4});


  auto& legs_index =  animals.get<1>();
  std::cout << legs_index.count(4) << std::endl;
  return 0;
}

输出:1

hashed_non_unique which calculates a hash value that does not have to be unique. In order to guarantee that no value is stored twice, use boost::multi_index::hashed_unique. If one interface does not allow you to store values multiple times, it does not matter whether another interface does allow it. The example tries to store a dog, which has the same number of legs as the already stored cat. Because this violates the requirement of having unique hash values for the second interface, the dog will not be stored in the container. Therefore, when searching for animals with four legs, the program displays 1, because only the cat was stored and counted.

3. The interfaces sequenced, ordered_noe_unique, random_access

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/member.hpp>
#include <string>
#include <iostream>

using namespace boost::multi_index;

struct animal {
  std::string name;
  int legs;
};

typedef multi_index_container<
  animal,
  indexed_by<
    sequenced<>,
    ordered_non_unique<
      member<
        animal, int, &animal::legs
      >
    >,
    random_access<>
  >
> animal_multi;

int main() {
  animal_multi animals;

  animals.insert({"cat", 4});
  animals.insert({"shark", 0});
  animals.insert({"spider", 8});

  auto& legs_index =  animals.get<1>();
  auto it = legs_index.lower_bound(4);
  auto end = legs_index.upper_bound(8);

  for (; it != end; ++it) {
    std::cout << it->name << std::endl;
  }
  
  const auto& rand_index = animals.get<2>();
  std::cout << rand_index[0].name << std::endl;
  return 0;
}

The interface boost::multi_index::sequenced allows you to treat a MultiIndex container like a list of type std::list. Elements are stored in the given order.

With the interface boost::multi_index::ordered_non_unique, objects are automatically sorted. This interface requires that you specify a sorting criterion when defining the container. ordered_non_unique provides special member functions to find specific ranges within the sorted values. Using lower_bound() and upper_bound(), the program searches for animals that have at lease four and no more than eight legs.

boost::multi_index::random_access allows you to treat the MultiIndex container like a vector of type std::vector. The two most prominent member functions are operator[] and at().

4. The key extractors identity and const_mem_fun

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <string>
#include <utility>
#include <iostream>

using namespace boost::multi_index;

class animal {
  public:
    animal(std::string name, int legs) : name_(std::move(name)), legs_(legs) {}
    bool operator<(const animal& a) const {
      return legs_ < a.legs_;
    }
    const std::string& name() const {
      return name_;
    }
  private:
    std::string name_;
    int legs_;
};

typedef multi_index_container<
  animal,
  indexed_by<
    ordered_unique<
      identity<animal>
    >,
    hashed_unique<
      const_mem_fun<
        animal, const std::string&, &animal::name
      >
    >
  >
> animal_multi;

int main() {
  animal_multi animals;

  animals.emplace("cat", 4);
  animals.emplace("shark", 0);
  animals.emplace("spider", 8);

  std::cout << animals.begin()->name() << std::endl;

  const auto& name_index = animals.get<1>();
  std::cout << name_index.count("shark") << std::enl;
  return 0;
}

boost::multi_index::identity uses elements stored in the container as keys. This requires the class animal to be sortable because objects of type animal will be used as the key for the interface boost::multi_index::ordered_unique. This is achieved through the overloaded operator<

boost::multi_index::const_mem_fun and boost::multi_index::mem_fun that use the return value of a member function as a key.

原文地址:https://www.cnblogs.com/sssblog/p/11004572.html