Boost Multi-index Containers boost::multi_index_container

25 篇文章 0 订阅

一、简介

引例

STL(standard template library)中的容器,如set, map,multimap, unordered_map等,可以存放key或key-value的数据, key是索引键,可以用来查找。

例如,为了记录员工信息:

    struct employee
    {
      int         id;
      std::string name;

      employee(int id,const std::string& name):id(id),name(name){}

      bool operator<(const employee& e)const{return id<e.id;}
    };

当我们既需要按id大小顺序访问,又需要按姓名的字母顺序访问,如果还用STL,就需要设计复杂的程序, 如:

  • 用std::vector按id存放employee对象, 并在一个set中存放按name排序的employee的指针。

此时可以考虑使用Boost库中的multi_index_container多索引容器。
举例:

#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>

using namespace ::boost;
using namespace ::boost::multi_index;

// define a multiply indexed set with indices by id and name
typedef multi_index_container<
  employee,
  indexed_by<
    // sort by employee::operator<
    ordered_unique<identity<employee> >,
    // sort by less<string> on name
    ordered_non_unique<member<employee,std::string,&employee::name> >
  > 
> employee_set;

void print_out_by_name(const employee_set& es)
{
  // get a view to index #1 (name)
  const employee_set::nth_index<1>::type& name_index=es.get<1>();
  // use name_index as a regular std::set
  std::copy(
    name_index.begin(),name_index.end(),
    std::ostream_iterator<employee>(std::cout));
}

从例子中可以看出定义multi_index_container要指定元素类型、索引类型。 上例中有两个随索引类型:ordered_unique<identity >和ordered_non_unique<member<employee,std::string,&employee::name> >。

索引

get()可以获取第N个索引, N从0到indexed_by<>里的index的个数。
不 过 , m u l t i _ i n d e x _ c o n t a i n e r 也 可 以 直 接 访 问 , 这 是 时 候 默 认 是 通 过 第 0 个 索 引 访 问 的 \color{red}{不过,multi\_index\_container也可以直接访问,这是时候默认是通过第0个索引访问的} multi_index_container访,0访
如下面这两种定义方法是等效的:

multi_index_container<(element)>
// is equialent to
multi_index_container<
  (element),
  indexed_by<
    ordered_unique<identity<(element)> >
  >
>

注 意 : g e t < N > ( ) 返 回 i n d e x 的 引 用 , 因 此 定 义 的 类 型 一 定 是 引 用 类 型 。 \color{red}{注意:get<N>()返回index的引用, 因此定义的类型一定是引用类型。} get<N>()index

multi_index_container实例的索引项通过index_by指定。 如上文中ordered_unique, ordered_non_unique。 这里先支出,这两个索引类型需要元素提供比较谓词(comparison predicate )御用元素排序。

标签 tag

get中N为从0开始的索引号, 如果索引比较多, 记住number有点困难,还可以选择性的为为索引增加标签。例如

// tags 
struct name{};

typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >,
    ordered_non_unique<tag<name>,member<employee,std::string,&employee::name> >
  >
> employee_set;

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name::iterator it=es.get<name>().begin();

此时get()和get<1>()是等效的。
而且一个索引项,可以有多个标签, 如:

// tags
struct name{};
struct by_name{};

typedef multi_index_container<
  ...
    ordered_non_unique<
      tag<name,by_name>, // two tags
      member<employee,std::string,&employee::name>
    >
  ...
> employee_set;
迭代器访问

不同的索引项有不同的迭代器类型。 如:

typedef employee_set::nth_index<1>::type employee_set_by_name;
employee_set_by_name::iterator it=
  es.get<1>().find("Judy Smith");
元素修改

所有索引的迭代器都是const的, 因此不能通过迭代器直接修改元素的值。 因为如果允许元素不加区别地修改,我们可以在不通知容器的情况下,在有序索引中引入不一致。
只能通过replace()或modify()的发方法修改元素。

二、索引

索引类型

目前Boost.MultiIndex提供以下索引类型:

  • ordered_unique and ordered_non_unique for ordered indices,
    元素像std::set一样存放,关键字需要有operator< 用于排序,包括ordered_unique, ordered_non_unique。
  • ranked_unique and ranked_non_unique for ranked indices,
    和std::distance相关,没用过。
  • hashed_unique and hashed_non_unique for hashed indices,
    hashed_index是按hash值索引的,类似与std::unordered_map, 关键字需要有operator()或std::hash用于计算hash值, 同样有hashed_unique和hashed_non_unique两种
  • sequenced for sequenced indices,
    可以使容器元素像list一样顺序访问;
  • random_access for random access indices.
    支持随机访问,像vector一样可以用下标访问, 下标按插入的顺序递增。

带unique的是唯一索引,关键字不可重复; 带non_unique的是非唯一索引, 关键字可以重复, 下同。类似于std::set、std::multiset.

索引的定义规则为(以ordered**为例),

(ordered_unique | ordered_non_unique)
  <[(tag)[,(key extractor)[,(comparison predicate)]]]>
关键值提取

索引的定义规则中的(key extractor),指定关键值提取。 可以是整个元素,如上例中的identity<employee>, 返回employee对象, 也可以是元素的一个成员变量,如上例中的member<employee,std::string,&employee::name>
除了identify和member, 还有更多的关键值提取方法,以后介绍。

比较谓词

ordered**有序索引的定义规则最后的(comparison predicate) 制定了关键字的比较方法,默认是按std::less<key_type>排序的, 但是也可以自动以比较方法, 如:

// define a multiply indexed set with indices by id and by name
// in reverse alphabetical order
typedef multi_index_container<
  employee,
  indexed_by<
    ordered_unique<identity<employee> >, // as usual
    ordered_non_unique<
      member<employee,std::string,&employee::name>,
      std::greater<std::string>  // default would be std::less<std::string>
    >
  >
> employee_set;
> 
查找、检索

可以按照索引项的关键值查找, 如

employee_set es;
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name::iterator it=es.get<name>().find("Veronica Cruz");

对于有序索引, 还提供lower_bound,和upper_bound两个函数,用于范围查找,用法如下:

employee_set::iterator p0=es.lower_bound(employee(0,""));
employee_set::iterator p1=es.upper_bound(employee(100,""));

而且lower_bound和upper_bound参数甚至可以不是关键值类型。 employee_set的第#0个关键值是employee, 假设我们只想按id查找,可以如下:

struct comp_id
{
  // compare an ID and an employee
  bool operator()(int x,const employee& e2)const{return x<e2.id;}

  // compare an employee and an ID
  bool operator()(const employee& e1,int x)const{return e1.id<x;}
};
// and now write the search as
employee_set::iterator p0=es.lower_bound(0,comp_id());
employee_set::iterator p1=es.upper_bound(100,comp_id());
更新元素

因为作为索引的关键值的返回的元素是const的,不能直接通过=运算符修改容器元素, 只能通过以下方式(除非把某个没有作为任何索引关键值的元素成员变量,声明为mutable的, 则可以通过iterator直接修改该成员变量):

  1. replace(),替换
auto it=name_index.find("Anna Jones");
employee anna=*it;
anna.name="Anna Smith";      // she just got married to Calvin Smith
name_index.replace(it,anna); // update her record
  • 替换操作如果不改变container的顺序,时间复杂度是O(1)的, 否则是O(logN)的。
  • 替换后,迭代器仍有效
  • 异常安全
  1. modify(), 修改
struct change_name
{
   change_name(const std::string& new_name):new_name(new_name){}
   void operator()(employee& e) // reference
 	{
   	e.name=new_name;
   }
private:
	std::string new_name;
};
...
auto it=name_index.find("Anna Jones");
name_index.modify(it,change_name("Anna Smith"));

此外还有类似的modify_key(), 它们都是异常不安全的。

顺序索引 Sequenced indices

类似与std::list, 使用如下:

multi_index_container<
  int,
  indexed_by<
    sequenced<>,           // sequenced type
    ordered_unique<identity<int> > // another index
  >
> s;
s.get<1>().insert(1); // insert 1 through index #1
s.get<1>().insert(0); // insert 0 through index #1
// list elements through sequenced index #0
std::copy(s.begin(),s.end(),std::ostream_iterator<int>(std::cout));
// result: 1 0

参考:
https://www.boost.org/doc/libs/1_62_0/libs/multi_index/doc/tutorial/index.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值