boost.png (6897 bytes)Boost.MultiIndex Tutorial: Techniques



Contents目錄

用 multi_index_container 替代標準容器

替代關聯式容器

先把理論放在一邊,用multi_index_container替代標準的關聯式容器有一個非常實際的原因,就是獲得multi_index_container所提供的查找、範圍查詢和更新等擴展功能的優點。

為了替代 std::set,要遵循以下替代規則:

std::set<Key,Compare,Allocator> ->
  multi_index_container<
    Key,
    indexed_by<ordered_unique<identity<Key>,Compare> >,
    Allocator
  >
  

缺省情況下,Compare=std::less<Key>,且 Allocator=std::allocator<Key>,替代規則可以簡化為:

std::set<Key> -> multi_index_container<Key>
  

這種 multi_index_containerstd::set 的替換,保留了std::set所提供的所有功能,因此原則上它是順便而為,不需要什麼調整。

std::multiset 也同樣可以被替換,規則如下:

std::multiset<Key,Compare,Allocator> ->
  multi_index_container<
    Key,
    indexed_by<ordered_non_unique<identity<Key>,Compare> >,
    Allocator
  >
  

同樣考慮到參數的缺省值,規則可以簡化為:

std::multiset<Key> ->
  multi_index_container<
    Key,
    indexed_by<ordered_non_unique<identity<Key> > >
  >
  

multi_index_container替代 std::multiset 其實在接口上是有那麼一點點不同的:成員函數 insert(const value_type&) 不再是返回std::multiset的一個iterator,而是象std::set那樣的一個std::pair<iterator,bool>。不過在實際使用中,返回值的 bool 成員總為 true

直接用multi_index_container替代 std::mapstd::multimap 卻是不行的。主要問題是multi_index_container中的元素被視為常量,而 std::mapstd::multimap 保存的對象類型則是 std::pair<const Key,T>,其中的值部分是允許隨意變動的。為了克服這個困難,我們需要創建一個 pair 類:

template <typename T1,typename T2>
struct mutable_pair
{
  typedef T1 first_type;
  typedef T2 second_type;

  mutable_pair():first(T1()),second(T2()){}
  mutable_pair(const T1& f,const T2& s):first(f),second(s){}
  mutable_pair(const std::pair<T1,T2>& p):first(p.first),second(p.second){}

  T1         first;
  mutable T2 second;
};
  

然後,替代規則如下:

std::map<Key,T,Compare,Allocator> ->
  multi_index_container<
    Element,
    indexed_by<
      ordered_unique<member<Element,Key,&Element::first>,Compare>
    >,
    typename Allocator::template rebind<Element>::other
  >

std::multimap<Key,T,Compare,Allocator> ->
  multi_index_container<
    Element,
    indexed_by<
      ordered_non_unique<member<Element,Key,&Element::first>,Compare>
    >,
    typename Allocator::template rebind<Element>::other
  >

(with Element=mutable_pair<Key,T>)

如果考慮參數的缺省值,規則可以簡化為:

std::map<Key,T> ->
  multi_index_container<
    Element,
indexed_by<ordered_unique<member<Element,Key,&Element::first> > > > std::multimap<Key,T> -> multi_index_container< Element,
indexed_by<ordered_non_unique<member<Element,Key,&Element::first> > > > (with Element=mutable_pair<Key,T>)

與標準的set不同,這些 multi_index_container替代物的接口並不嚴格符合std::mapstd::multimap。最為明顯的差別是沒有了 operator [],無論是讀或寫模式;不過,可以用適當的 findinsert 來替代。

這些以multi_index_container實現的關聯式容器在空間和時間複雜度上都與標準的對照物相當。有關更為詳細的討論,請查閱“性能”一節。

替代 std::list

與關聯式容器不同,用Boost.MultiIndex替代 std::list 並沒有增加任何有用的功能,所以以下討論完全是出於興趣。

與標準的map相似,替代std::list的主要困難來自於multi_index_container中的元素是常量的。因此,這裡又再需要某種適配的類,如下:

template <typename T>
struct mutable_value
{
  mutable_value(const T& t):t(t){}
  operator T&()const{return t;}

private:
  mutable T t;
};
  

這樣我們可以得到以下替代規則:

std::list<T,Allocator> ->
  multi_index_container<
    Element,
    indexed_by<sequenced<> >,
    typename Allocator::template rebind<Element>::other
  >

(with Element=mutable_value<T>)

或者,如果缺省值 Allocator=std::allocator<T>,可以簡化為:

std::list<T> ->
  multi_index_container<mutable_value<T>,indexed_by<sequenced<> > >
  

元編程與 multi_index_container

Boost.MultiIndex 提供了大量工具,以通過MPL元編程實現 multi_index_container 實例化的分解與合成。

MPL 分解

給定一個 multi_index_container 實例化,在編譯期可使用以下嵌套類型檢視multi_index_container定義中的各種類型:

以上每一個類型都是一個MPL序列,包含了組成multi_index_container的多個索引,例如:iterator_type_list 的第n個元素就是 nth_index_iterator<n>::type

index_specifier_type_listindex_type_list之間有一個微妙而重要的差異:前者持有multi_index_container實例化所定義的各個索引說明,後者則用於訪問每個索引說明所對應的實際類。舉個例子可以有助於理解這個差異。給定如下實例化:

typedef multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >,
    sequenced<>
  >
> indexed_t;
  

indexed_t::index_specifier_type_list 類型列表中的元素如下:

ordered_unique<identity<int> >
sequenced<>
  

indexed_t::index_type_list 的類型元素如下:

multi_index_container::nth_type<0>::type
multi_index_container::nth_type<1>::type
  

因此這兩個類型列表是完全不同的。這些類型列表所相關的MPL序列概念,請查閱參考一節。

MPL 合成

雖然索引通常都是用indexed_by結構來指定,但實際上任何由索引說明組成的MPL序列都可以使用:

typedef mpl::vector<ordered_unique<identity<int> >,sequenced<> > index_list_t;

typedef multi_index_container<
  int,
  index_list_t
> indexed_t;
  

這樣就可以通過MPL元編程來合成multi_index_container 的實例化,如下例所示:

// 原先的 multi_index_container 實例化
typedef multi_index_container<
  int,
  indexed_by<
    ordered_unique<identity<int> >
  >
>                                indexed_t1;

// 取出它的索引列表並增加一個索引
typedef boost::mpl::push_front<
  indexed_t1::index_specifier_type_list,
  sequenced<>
>::type                          index_list_t;

// 擴展後的 multi_index_container
typedef multi_index_container<
  int,
  index_list_t
>                                indexed_t2;
  



Revised February 6th 2006

© Copyright 2003-2006 Joaquín M López Muñoz. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)