Boost.MultiIndex Tutorial: Techniquesmulti_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_container 對 std::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::map 和
std::multimap
卻是不行的。主要問題是multi_index_container中的元素被視為常量,而 std::map 和
std::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::map和std::multimap。最為明顯的差別是沒有了
operator [],無論是讀或寫模式;不過,可以用適當的 find 和
insert 來替代。
這些以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 實例化的分解與合成。
給定一個 multi_index_container
實例化,在編譯期可使用以下嵌套類型檢視multi_index_container定義中的各種類型:
index_specifier_type_list,index_type_list,iterator_type_list,const_iterator_type_list.multi_index_container的多個索引,例如:iterator_type_list
的第n個元素就是 nth_index_iterator<n>::type。
在index_specifier_type_list 和
index_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序列概念,請查閱參考一節。
雖然索引通常都是用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)