Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

Boost.MultiIndex 教程:容器创建



目录

值语义

multi_index_container 具有与复制构造和赋值相关的常用值语义,即,源容器中元素的副本被创建并插入到目标容器中。更有趣的是,复制还会为容器的每个索引重新创建元素排列的原始顺序。这意味着,对于定义了相等性的索引类型,所有索引的相等性在复制或赋值时都得到保留。这种行为可以被视为对复制语义一般规则的自然扩展,该规则声明如果 yx 的副本,则 y==x

ctor_args_list 的用法

虽然在大多数情况下,multi_index_container 将会被默认构造(或从预先存在的 multi_index_container 复制),但有时有必要为使用的内部对象(键提取器、比较谓词、分配器)指定特定值,例如,如果其中一些对象没有默认构造函数。标准 STL 容器也可能出现相同的情况,它们允许可选地指定此类对象

// example of non-default constructed std::set
template<typename IntegralType>
struct modulo_less
{
  modulo_less(IntegralType m):modulo(m){}

  bool operator()(IntegralType x,IntegralType y)const
  {
    return (x%modulo)<(y%modulo);
  }

private:
  IntegralType modulo;
};

typedef std::set<unsigned int,modulo_less<unsigned int> > modulo_set;

modulo_set m(modulo_less<unsigned int>(10));

multi_index_container 也提供了此功能,尽管方式要复杂得多,因为 multi_index_container 的构造函数必须接受其索引的所有内部对象的值。multi_index_container 构造函数的完整形式是

explicit multi_index_container(
    const ctor_args_list& args_list=ctor_args_list(),
    const allocator_type& al=allocator_type());

分配器对象的规范没有特别的问题;至于 ctor_args_list,此对象被设计为保存 multi_index_container 中每个索引的必要构造值。从用户的角度来看,ctor_args_list 等同于类型

boost::tuple<C0,...,CI-1>

其中 I 是索引的数量,而 Ci

nth_index<i>::type::ctor_args

也就是说,第 i 个索引的嵌套类型 ctor_args。每个 ctor_args 类型本身是一个元组,其中包含关联索引的构造函数参数的值:因此,有序索引需要键提取器对象和比较谓词,哈希索引需要初始桶数、键提取器、哈希函数和相等谓词;而序列索引和随机访问索引不需要任何构造参数。例如,给定定义

typedef multi_index_container<
  unsigned int,
  indexed_by<
    hashed_unique<identity<unsigned int> >,
    ordered_non_unique<identity<unsigned int>, modulo_less<unsigned int> >,
    sequenced<>,
    random_access<>
  >
> modulo_indexed_set;

相应的 ctor_args_list 类型等同于

boost::tuple<
  // ctr_args of index #0
  boost::tuple<
    std::size_t, // initial number of buckets; 0 if unspecified
    identity<unsigned int>,
    boost::hash<unsigned int>,
    std::equal_to<unsigned int> >,    

  // ctr_args of index #1
  boost::tuple<
    identity<unsigned int>,
    modulo_less<unsigned int> >,
  
  // sequenced indices do not have any construction argument
  boost::tuple<>,

  // neither do random access indices
  boost::tuple<>
>

这样的 modulo_indexed_set 不能默认构造,因为 modulo_less 没有提供默认构造函数。下面展示了如何进行构造

modulo_indexed_set::ctor_args_list args_list=
  boost::make_tuple(
    // ctor_args for index #0 is default constructible
    modulo_indexed_set::nth_index<0>::type::ctor_args(),
    
    boost::make_tuple(identity<unsigned int>(),modulo_less<unsigned int>(10)),
    
    // these are also default constructible (actually, empty tuples) 
    modulo_indexed_set::nth_index<2>::type::ctor_args(),
    modulo_indexed_set::nth_index<3>::type::ctor_args()
  );

modulo_indexed_set m(args_list);

示例部分中提供了一个程序,用于实践这些概念。

特殊分配器支持

正如 参考 中详细解释的那样,Boost.MultiIndex 允许使用比 C++ 标准严格要求的更通用的分配器类。支持的一种重要的非标准分配器是由 Boost Interprocess Library 提供的分配器;这为将 multi_index_container 放置在共享内存中提供了可能性。

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

namespace bip=boost::interprocess;

// a shared memory compatible allocator of ints
typedef bip::allocator<
  int,bip::managed_shared_memory::segment_manager
> shared_int_allocator;

// define a shared memory compatible multi_index_container
// using shared_int_allocator
typedef multi_index_container<
  int,
  indexed_by<
    sequenced<>,
    ordered_unique<identity<int> >
  >,
  shared_int_allocator
> unique_int_list;

...

// create a managed memory segment
bip::managed_shared_memory seg(
  bip::create_only,"SharedMemoryID",65536);

// construct a unique_int_list into the segment
unique_int_list* puil=seg.construct<unique_int_list>
  ("UniqueIntListID") // object identifier within the segment
  // Construction args: first a ctor arg list, then a
  // shared memory allocator obtained from the segment object.
  (unique_int_list::ctor_args_list(),
   unique_int_list::allocator_type(seg.get_segment_manager()));
   

示例部分包含一个 程序,进一步探讨了此功能。

序列化

可以通过 Boost Serialization Library 存档和检索 multi_index_container。支持常规和 XML 存档。用法很简单,与任何其他可序列化类型没有什么不同。例如

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>

...

void save(const employee_set& es)
{
  std::ofstream ofs("data");
  boost::archive::text_oarchive oa(ofs);
  oa<<es;
}

void load(employee_set& es)
{
  std::ifstream ifs("data");
  boost::archive::text_iarchive ia(ifs);
  ia>>es;
}

...

employee_set es;
... // fill it with data
save(es);

...

employee_set restored_es;
load(restored_es);

通过仅链接到适当的 Boost.Serialization 库模块,即可自动提供序列化功能:除了声明过程中使用的存档类型的标头之外,无需显式包含 Boost.Serialization 中的任何标头。但是,如果未使用,则可以通过全局定义宏 BOOST_MULTI_INDEX_DISABLE_SERIALIZATION 来禁用序列化支持。禁用 Boost.MultiIndex 的序列化可以稍微提高构建时间,并且在那些无法正确处理 Boost.Serialization 标头的有缺陷的编译器中可能是必需的。

根据 Boost.MultiIndex 的 值语义,检索存档的 multi_index_container 不仅恢复元素,还恢复它们在容器的每个索引中排列的顺序。但是,此规则有一个例外:对于 哈希索引,不保证元素在恢复的容器中迭代的顺序;通常,依赖哈希索引的元素顺序是不明智的,因为它会在插入或重新哈希期间以任意方式更改——这正是哈希索引和 TR1 无序关联容器未定义相等运算符的原因。

multi_index_container 的索引的迭代器也可以被序列化。迭代器的序列化必须仅在序列化其对应的容器之后进行。

示例 9 在示例部分中展示了 Boost.MultiIndex 的序列化功能。




修订于 2007 年 7 月 17 日

© 版权所有 2003-2007 Joaquín M López Muñoz。根据 Boost 软件许可,版本 1.0 分发。(请参阅随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)