Boost C++ 函式庫

...世界上最受推崇且設計最精良的 C++ 函式庫專案之一。 Herb SutterAndrei Alexandrescu, C++ 編碼規範

賦值函式庫

版權所有 © 2003-2006 Thorsten Ottosen

使用、修改和散佈受 Boost 軟體授權條款 1.0 版約束(請參閱 https://boost.dev.org.tw/LICENSE_1_0.txt)。

目錄


簡介

operator,() 的實際用途似乎不多.
Bjarne Stroustrup,《C++ 的設計與演化》

這個函式庫的目的是透過重載 operator,()operator()(),讓使用資料填充容器變得容易。這兩個運算子使得建構數值列表成為可能,然後這些數值會被複製到容器中。

這些列表在學習、測試和原型設計的情況下特別有用,但也可以在其他情況下派上用場。該函式庫附帶了標準函式庫容器的預定義運算子,但大多數功能將適用於任何符合標準的容器。該函式庫也使得擴充使用者定義的類型成為可能,因此例如可以為數值列表呼叫成員函式,而不是其一般引數。


教學

在兩分鐘內,您應該就能使用這個函式庫。以下章節說明了主要元件

前兩個函式用於在建立容器物件後加入元素,而接下來的兩個函式則用於在我們需要初始化物件時使用。

函式 operator+=()

要使用 operator+=() 填充向量(或任何標準容器)的值,您可以寫

#include <boost/assign/std/vector.hpp> // for 'operator+=()'
#include <boost/assert.hpp>
using namespace std;
using namespace boost::assign; // bring 'operator+=()' into scope

{
    vector<int> values;  
    values += 1,2,3,4,5,6,7,8,9; // insert values at the end of the container
    BOOST_ASSERT( values.size() == 9 );
    BOOST_ASSERT( values[0] == 1 );
    BOOST_ASSERT( values[8] == 9 );
}
這裡我們只將常數填入容器中,但只要每個運算式的結果可以轉換為容器的 value_type,該列表就可以包含任意的運算式。

函式 operator()()

我們不直接呼叫 operator()(),而是呼叫一個返回代理物件的函式,該代理物件定義了 operator()()。返回代理物件的函式始終以用於將列表中的值複製到容器中的成員函式命名。因此,要用成對的值填充映射,您可以寫

#include <boost/assign/list_inserter.hpp> // 用於 'insert()' #include <boost/assert.hpp> #include <string> using namespace std; using namespace boost::assign; // 將 'insert()' 帶入範圍  { map<string,int> months; insert( months ) ( "january", 31 )( "february", 28 ) ( "march", 31 )( "april", 30 ) ( "may", 31 )( "june", 30 ) ( "july", 31 )( "august", 31 ) ( "september", 30 )( "october", 31 ) ( "november", 30 )( "december", 31 ); BOOST_ASSERT( months.size() == 12 ); BOOST_ASSERT( months["january"] == 31 ); } 
請注意,當我們需要使用多個引數(預設支援最多五個引數,但限制可以
自訂)建構物件時,operator()() 會方便得多。序列也是如此
#include <boost/assign/list_inserter.hpp> // for 'push_front()'
#include <boost/assert.hpp> 
#include <string>
#include <utility>
using namespace std;
using namespace boost::assign; // bring 'push_front()' into scope
 
{
    typedef pair< string,string > str_pair;
    deque<str_pair> deq;
    push_front( deq )( "foo", "bar")( "boo", "far" ); 
    BOOST_ASSERT( deq.size() == 2 );
    BOOST_ASSERT( deq.front().first == "boo" );
    BOOST_ASSERT( deq.back().second == "bar" );
}   
除了 push_front() 之外,如果容器有對應的成員函式,我們也可以使用 push_back()。空括號可用於插入預設建構的物件,例如,push_front( deq )()() 將插入兩個預設建構的 str_pair 物件。

如果 operator()()push_front() 等一起使用太麻煩,我們也可以說

deque<int> di;    
push_front( di ) = 1,2,3,4,5,6,7,8,9;
BOOST_ASSERT( di.size() == 9 );    
BOOST_ASSERT( di[0] == 9 );    

為了清楚起見,上面的程式碼不限於標準容器,但會與所有具有正確成員函式的符合標準的容器一起使用。只有 operator+=() 被限制在標準容器中。

函式 list_of()

但是,如果我們需要初始化容器呢?這就是 list_of() 發揮作用的地方。使用 list_of(),我們可以建立自動轉換為任何容器的匿名列表
#include <boost/assign/list_of.hpp> // for 'list_of()'
#include <boost/assert.hpp> 
#include <list>
#include <stack>
#include <string>
using namespace std;
using namespace boost::assign; // bring 'list_of()' into scope
 
{
    const list<int> primes = list_of(2)(3)(5)(7)(11);
    BOOST_ASSERT( primes.size() == 5 );
    BOOST_ASSERT( primes.back() == 11 );
    BOOST_ASSERT( primes.front() == 2 );
   
    const stack<string> names = list_of( "Mr. Foo" )( "Mr. Bar")( "Mrs. FooBar" ).to_adapter();
    const stack<string> names2 = (list_of( "Mr. Foo" ), "Mr. Bar", "Mrs. FooBar" ).to_adapter();
    BOOST_ASSERT( names.size() == 3 );
    BOOST_ASSERT( names.top() == "Mrs. FooBar" );
}   
如果我們需要初始化容器配接器,我們需要透過呼叫 to_adapter() 來幫助編譯器。如第二個範例所示,如果我們在整個右側加上括號,則可以使用帶有 list_of() 的逗號分隔列表。值得注意的是,list_of() 的第一個引數決定了匿名列表的類型。就堆疊而言,匿名列表由 const char* 物件組成,然後將其轉換為 string 物件的堆疊。只要儲存類型之間的轉換是可能的,轉換總是可能的。

請注意,list_of() 甚至可以轉換為 boost::array<T,sz>,並查看 支援的函式庫清單。

請注意,list_of()(及其變體)返回的類型具有重載的比較運算子。這允許您編寫測試程式碼,例如 BOOST_CHECK_EQUAL( my_container, list_of(2)(3)(4)(5) );.

函式 map_list_of()

此函式在處理映射時為了方便而定義。它的用法很簡單
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
#include <boost/assert.hpp> 
#include <map>
using namespace std;
using namespace boost::assign; // bring 'map_list_of()' into scope
 
{
    map<int,int> next = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);
    BOOST_ASSERT( next.size() == 5 );
    BOOST_ASSERT( next[ 1 ] == 2 );
    BOOST_ASSERT( next[ 5 ] == 6 );
    
    // or we can use 'list_of()' by specifying what type
    // the list consists of
    next = list_of< pair<int,int> >(6,7)(7,8)(8,9);
    BOOST_ASSERT( next.size() == 3 );
    BOOST_ASSERT( next[ 6 ] == 7 );
    BOOST_ASSERT( next[ 8 ] == 9 );      
}   
也可以使用函式 pair_list_of()

函式 tuple_list_of()

如果您正在使用元組,則使用 tuple_list_of() 可能很方便
#include <boost/assign/list_of.hpp>
#include <vector>

using namespace std;
using namespace boost::assign;

{
    typedef boost::tuple<int,std::string,int> tuple;

    vector<tuple> v = tuple_list_of( 1, "foo", 2 )( 3, "bar", 4 );
    BOOST_CHECK( v.size() == 2 );
    BOOST_CHECK( boost::get<0>( v[1] ) ==  3 );
}
    

函式 repeat()repeat_fun()range()

有時,多次重複相同的值太令人煩躁。這就是 repeat() 可以派上用場的地方

#include <boost/assign/list_of.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/assert.hpp>

using namespace std;
using namespace boost::assign;
 
{
    vector<int> v;
    v += 1,2,3,repeat(10,4),5,6,7,8,9;
    // v = [1,2,3,4,4,4,4,4,4,4,4,4,4,5,6,7,8,9]
    BOOST_ASSERT( v.size() == 3 + 10 + 5 );
    
    v = list_of(1).repeat(5,2)(3);
    // v = [1,2,2,2,2,2,3]
    BOOST_ASSERT( v.size() == 1 + 5 + 1 );
    
    push_back( v )(1).repeat(1,2)(3);
    // v = old v + [1,2,3]
    BOOST_ASSERT( v.size() == 10 );
}
如我們所見,repeat() 的第一個引數是要重複第二個引數的次數。

可以使用 repeat_fun() 建構更通用的列表

#include <boost/assign/std/vector.hpp>
#include <boost/assert.hpp>
#include <cstdlib> // for 'rand()'              

using namespace std;
using namespace boost::assign;
 
template< class T >
struct next    
{
    T seed;
    next( T seed ) : seed(seed) 
    { }
    
    T operator()() 
    {
        return seed++;
    }
};
     
{
    vector<int> v;
    v += 1,2,repeat_fun(4,&rand),4;
    // v = [1,2,?,?,?,?,4] 
    BOOST_ASSERT( v.size() == 7 );
    
    push_back( v ).repeat_fun(4,next<int>(0))(4).repeat_fun(4,next<int>(5));
    // v = old v + [0,1,2,3,4,5,6,7,8] 
    BOOST_ASSERT( v.size() == 16 );
}        
repeat_fun() 的第二個引數的唯一要求是它是零元函式。

如果您只需要將迭代器範圍插入列表中的某個位置,則成員函式 range() 提供了您想要的內容。它基於 Boost.Range,因此您可以傳遞該函式庫支援的所有範圍。例如

#include <boost/assign/list_inserter.hpp> // for 'push_back()'
#include <boost/assign/list_of.hpp>       // for 'list_of()' and 'ref_list_of()'
#include <boost/assert.hpp>

using namespace std;
using namespace boost::assign;
 
{
    vector<int> v, v2;
    v  = list_of(1)(2)(3);
    v2 = list_of(0).range(v).range(v.begin(),v.end())(4);
    // v2 = [0,1,2,3,1,2,3,4]
    BOOST_ASSERT( v2.size() == 8u );

    push_back( v ).range(v2)(5);
    // v = [1,2,3,0,1,2,3,1,2,3,4,5]
    BOOST_ASSERT( v.size() == 12u ); 

    //
    // create a list of references, some coming from a container, others from the stack 
    //
    int x = 0;
    int y = 1;
    BOOST_ASSERT( ref_list_of<10>(x).range(v2)(y).size() == 10u );
}
如您所見,如果更合適,也可以傳遞兩個迭代器。最後一個範例還介紹了參考列表。更多關於以下內容。

函式 ref_list_of()cref_list_of()

當您需要建立值的匿名範圍並且速度至關重要時,這兩個函式提供了您想要的內容。
#include <boost/assign/list_of.hpp>
#include <algorithm>

//
// Define Range algorithm
//
template< class Range >
typename Range::const_iterator max_element( const Range& r )
{
    return std::max_element( r.begin(), r.end() );
}

using namespace boost::assign;

{
    int a=1,b=5,c=3,d=4,e=2,f=9,g=0,h=7;
    int& max = *max_element( ref_list_of<8>(a)(b)(c)(d)(e)(f)(g)(h) );
    BOOST_CHECK_EQUAL( max, f );
    max = 8;
    BOOST_CHECK_EQUAL( f, 8 );
    const int& const_max = *max_element(cref_list_of<8>(a)(b)(c)(d)(e)(f)(g)(h) );
    BOOST_CHECK_EQUAL( max, const_max );
}
    
您只能將左值與 ref_list_of() 一起使用,而 cref_list_of() 也接受右值。請勿擔心沒有準確指定正確的大小;使用的額外空間最少,且沒有與之相關的執行階段開銷。如果速度至關重要,您也可以使用這些函式代替 list_of()

一個「複雜」的範例

作為最後一個範例,讓我們假設我們需要追蹤足球比賽的結果。如果球隊獲勝,它將獲得一分,否則為零分。如果每個組別都打了三場比賽,程式碼可能如下所示

#include <boost/assign/list_of.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/assert.hpp>
#include <string>
#include <vector>

using namespace std;
using namespace boost::assign;
 
{
    typedef vector<int>                   score_type;
    typedef map<string,score_type>        team_score_map;
    typedef pair<string,score_type>       score_pair;

    team_score_map group1, group2;
    
    //
    // method 1: using 'insert()'
    //
    insert( group1 )( "Denmark", list_of(1)(1) )
                    ( "Germany", list_of(0)(0) )
                    ( "England", list_of(0)(1) );
    BOOST_ASSERT( group1.size() == 3 );
    BOOST_ASSERT( group1[ "Denmark" ][1] == 1 );
    BOOST_ASSERT( group1[ "Germany" ][0] == 0 );
    
    //
    // method 2: using 'list_of()'
    //
    group2 = list_of< score_pair >
                        ( "Norway",  list_of(1)(0) )
                        ( "USA",     list_of(0)(0) )
                        ( "Andorra", list_of(1)(1) );
    BOOST_ASSERT( group2.size() == 3 );
    BOOST_ASSERT( group2[ "Norway" ][0] == 1 );
    BOOST_ASSERT( group2[ "USA" ][0] == 0 );
}
    
在第一個範例中,請注意 list_of() 的結果如何自動轉換為 vector<int>,因為 insert() 知道它需要 vector<int>。在第二個範例中,我們可以看到 list_of() 有點不太聰明,因為這裡需要明確告知它預期什麼引數。(未來,可能可以在 list_of() 中引入更智慧的轉換層。)

函式 ptr_push_back()、ptr_push_front()ptr_insert()ptr_map_insert()

為了與 Boost.Pointer Container 一起使用,提供了一些特殊的例外安全函式。使用這些函式,您不需要手動呼叫 new
#include <boost/assign/ptr_list_inserter.hpp> // for 'ptr_push_back()', 'ptr_insert()' and 'ptr_push_front()'
#include <boost/assign/ptr_map_inserter.hpp>  // for 'ptr_map_insert()'
#include <boost/ptr_container/ptr_deque.hpp>
#include <boost/ptr_container/ptr_set.hpp>
#include <boost/ptr_container/ptr_map.hpp>

//
// Example class
//
struct Foo
{
    int i;
    
    Foo() : i(0)
    { }
    Foo( int i ) : i(i)
    { }
    Foo( int i, int ) : i(i)
    { }
    Foo( const char*, int i, int ) : i(i)
    { }

    virtual ~Foo()
    {}
};

struct Bar : Foo
{
    Bar()
    { }
    
    Bar( int i ) : Foo( 42 )
    { }
};

//
// Required by ptr_set<Foo>
//
inline bool operator<( Foo l, Foo r )
{
    return l.i < r.i;
}

 
using namespace boost;
using namespace boost::assign;

int main()
{
    ptr_deque<Foo> deq;
    ptr_push_back( deq )()();
    BOOST_ASSERT( deq.size() == 2u );
    ptr_push_back<Bar>( deq )()(); // insert 'Bar' objects
    BOOST_ASSERT( deq.size() == 4u );
    ptr_push_front( deq )( 3 )( 42, 42 )( "foo", 42, 42 );
    BOOST_ASSERT( deq.size() == 7u );

    ptr_set<Foo> a_set;
    ptr_insert( a_set )()( 1 )( 2, 2 )( "foo", 3, 3 );
    BOOST_ASSERT( a_set.size() == 4u );
    ptr_insert( a_set )()()()();
    BOOST_ASSERT( a_set.size() == 4u ); // duplicates not inserted
    ptr_insert<Bar>( a_set )( 42 ); // insert a 'Bar' object 
    BOOST_ASSERT( a_set.size() == 5u );

    ptr_map<int,Foo> a_map;
    ptr_map_insert( a_map )( 1 )( 2, 2 )( 3, 3, 3 )( 4, "foo", 4, 4 );
    ptr_map_insert<Bar>( a_map )( 42, 42 ); // insert a  'Bar' object
}
    
請注意,您可以為這些函式提供範本引數。此引數決定了要使用 new 分配的類型。當容器基於抽象類型時,您必須指定此引數(因為無法建立此類型的物件)。

對於 ptr_map_insert(),引數元組 (arg1,arg2,...,argN) 中的第一個引數 arg1 用於建構索引鍵;這表示第一個引數只需要可轉換為容器的 key_type。其餘引數用於建構映射的物件。

函式 ptr_list_of()

就像您可以使用 list_of() 來初始化容器一樣,您可以使用 ptr_list_of() 來初始化指標容器。以下是一個簡單的範例:
#include <boost/assign/ptr_list_of.hpp>
#include <boost/ptr_container/ptr_deque.hpp>

using namespace boost;
using namespace boost::assign;

{
    ptr_deque<Foo> deq;
    deq = ptr_list_of<Foo>( 42 )()()( 3, 3 )( "foo", 2, 1 );
    BOOST_CHECK( deq.size() == 5 );
}    
    
請注意,可以添加一個尾隨的 .to_container(deq) 來幫助許多較差的編譯器理解轉換(少數編譯器可以正確理解)。另請注意,不支援指標映射。

就這樣;現在您可以使用這個函式庫了。


參考

值得注意的是這個函式庫的實作方式。一個獨立函式(例如 push_back()operator+=())會回傳一個代理物件,該物件負責插入或賦值。代理物件透過重載 operator,()operator()(),以及從這些運算子內部呼叫「insert」函式來完成插入或賦值。「insert」函式通常透過使用 boost:: function 儲存在代理物件中。

通常不鼓勵重載 operator,(),因為它可能導致意想不到的結果,但是這個函式庫採用的方法是安全的,因為使用者永遠不會直接處理具有重載 operator,() 的物件。但是,您應該注意這一點。

逗號分隔列表中的運算式不再遵循內建逗號運算子的規則。這表示逗號分隔列表中運算式的求值順序是未定義的,就像指定函式引數列表一樣。

本文檔中的大多數程式碼在範例中使用 int,但當然它適用於任意類型,只要它們是可複製建構的即可。插入的資料不必是常數資料,可以是變數或從函式回傳的資料;唯一的要求是資料的類型可以轉換為儲存在容器中的類型。

所有轉發都是透過傳遞 const 參考的物件來完成的。最初,引數是透過傳值傳遞的(在 tuple_list_of() 中仍然如此)。要記住的一件事是,可以使用 boost:: ref 來傳遞參考。

所有內容都放在命名空間 boost::assign 中。

更多詳細資訊可以在下面找到

標頭

下面給出了此函式庫中標頭的概述。請注意,每個定義 operator+=() 的標頭都包含了 <boost/assign/list_inserter.hpp>

標頭 包含
<boost/assign.hpp> 除了指標容器的支援之外的所有內容
<boost/assign/list_of.hpp> list_of()map_list_of()tuple_list_of()ref_list_of() 和 cref_list_of()
<boost/assign/std.hpp> 所有標準容器的 operator+=() (請參閱下文)
<boost/assign/std/deque.hpp> std::dequeoperator+=()<deque>
<boost/assign/std/list.hpp> std::listoperator+=()<list>
<boost/assign/std/map.hpp> std::mapstd::multimapoperator+=()<map>
<boost/assign/std/queue.hpp> std::queuestd::priority_queueoperator+=()<queue>
<boost/assign/std/set.hpp> std::setstd::multisetoperator+=()<set>
<boost/assign/std/slist.hpp> 如果類別可用,則為 std::slistoperator+=()<slist>
<boost/assign/std/stack.hpp> std::stackoperator+=()<stack>
<boost/assign/std/vector.hpp> std::vectoroperator+=()<vector>
<boost/assign/assignment_exception.hpp> 類別 assignment_exception,可能由 list_of() 回傳的代理物件拋出
<boost/assign/list_inserter.hpp> 函式 make_list_inserter()push_back()push_front()insert()push() 和類別 list_inserter,這是整個函式庫的基礎。
<boost/assign/ptr_list_inserter.hpp> 函式 ptr_push_back()ptr_push_front()ptr_insert()
<boost/assign/ptr_map_inserter.hpp> 函式 ptr_map_insert()
<boost/assign/ptr_list_of.hpp> 函式 ptr_list_of()

標準容器

在以下內容中,三個點 (...) 表示實作定義。operator+=() 回傳一個代理物件,該物件會將呼叫轉發至 push_back()insert()push(),具體取決於容器支援的操作。

概要

namespace boost
{
namespace assign
{
    template< class V, class A, class V2 >
    list_inserter< ... >    operator+=( std::deque<V,A>& c, V2 v );
    
    template< class V, class A, class V2 >
    list_inserter< ... >    operator+=( std::list<V,A>& c, V2 v );
    
    template< class K, class V, class C, class A, class P >
    list_inserter< ... >    operator+=( std::map<K,V,C,A>& m, const P& p );
    
    template< class K, class V, class C, class A, class P >
    list_inserter< ... >    operator+=( std::multimap<K,V,C,A>& m, const P& p );
    
    template< class V, class C, class V2 >
    list_inserter< ... >    operator+=( std::queue<V,C>& c, V2 v );
    
    template< class V, class C, class V2 >
    list_inserter< ... >    operator+=( std::priority_queue<V,C>& c, V2 v );
    
    template< class K, class C, class A, class K2 >
    list_inserter< ... > operator+=( std::set<K,C,A>& c, K2 k );
    
    template< class K, class C, class A, class K2 >
    list_inserter< ... > operator+=( std::multiset<K,C,A>& c, K2 k );
    
    #ifdef BOOST_HAS_SLIST
              
    template< class V, class A, class V2 >
    list_inserter< ... >    operator+=( std::slist<V,A>& c, V2 v );
    
    #endif
    
    template< class V, class C, class V2 >
    list_inserter< ... >    operator+=( std::stack<V,C>& c, V2 v );
    
    template< class V, class A, class V2 >
    list_inserter< ... >    operator+=( std::vector<V,A>& c, V2 v );    

} // namespace 'assign'
} // namespace 'boost'  
請注意,額外的範本引數 V2 等是必要的,以允許可轉換為 V 的類型。

函式 list_of()map_list_of()

這兩個函式用於建構匿名列表,該列表可以轉換為任何標準容器和 boost::array<T,sz>。 這兩個函式回傳的物件保證具有以下描述的介面。

概要
namespace boost  
{
namespace assign
{
    template< class T >
    class Implementation-defined
    {
    public:
        const_iterator  begin() const;
        const_iterator  end() const;
        
        template< class U >
        Implementation-defined& operator,( U u );
        
        // inserts default-constructed object
        Implementation-defined& operator()();  

        template< class U >
        Implementation-defined& operator()( U u );
        
        template< class U, class U2 >
        Implementation-defined& operator()( U u, U2 u2 );

        //
        // and similarly up to 5 arguments
        //
        
        //
        // Convert to a 'Container'. 'Container' must have a constructor 
        // which takes two iterators.  
        //
        template< class Container >
        operator Container() const; 

        //
        // Convert to a container adapter like 'std::stack<>'.
        //
        Convertible-to-adapter to_adapter() const;
        
        //
        //
        // Convert to eg. 'boost::array<T,std::size_t>'. If the  
        // assigned variable is too small, 
        // a assignment_exception is thrown.
        // If the assigned variable it is too big, the rest of the 
        // values are  default-constructed.
        //
        template< template <class,std::size_t> class Array, class U, std::size_t sz > 
        operator Array<U,sz>() const;
    };
    
    //
    // Comparison operators. 'op' can be <,>,<=,>=,==,!=
    //
    template< class Range >
    bool op( const Implementation-defined&, const Range& );
    template< class Range >
    bool op( const Range&, const Implementation-defined& );
    
    template< class T >
    Implementation-defined   list_of();

    template< class T >
    Implementation-defined   list_of( T t );
    
    template< class T, class U, class U2 >
    Implementation-defined   list_of( U u, U2 u2 );
    
    template< class T, class U, class U2, class U3 >
    Implementation-defined   list_of( U u, U2 u2, U3 u3 );

    template< class T, class U, class U2, class U3, class U4 >
    Implementation-defined   list_of( U u, U2 u2, U3 u3, U4 u4 );
  
    template< class T, class U, class U2, class U3, class U4, class U5 >
    Implementation-defined   list_of( U u, U2 u2, U3 u3, U4 u4, U5 u5 );

    template< class Key, class T >
    Implementation-defined   map_list_of( Key k, T t )
    {
        return list_of< std::pair<Key,T> >()( k, t );
    }
} // namespace 'assign'
} // namespace 'boost'  

函式 repeat()repeat_fun()range()

這前兩個函式既以獨立函式的形式存在,也以 list_of() 回傳的物件和 list_inserter 的成員函式的形式存在。獨立版本用於建立 operator,() 的掛鉤,因此我們可以在逗號列表中間呼叫函式。當我們需要在括號列表中間呼叫函式時,使用成員函式。在這兩種情況下,我們都有

函式 range() 僅以成員函式的形式存在。提供以下兩個多載

template< class SinglePassIterator >
Implementation-defined range( SinglePassIterator first, SinglePassIterator last );

template< class SinglePassRange >
Implementation-defined range( const SinglePassRange& rng );

類別 list_inserter

此類別負責將元素插入容器中,並且是將函式庫擴展為支援您喜愛的類別的關鍵。

概要

namespace boost
{
namespace assign
{
    template< Function, Argument = void > 
    class list_inserter
    {
        Function fun;
        
    public:
        explicit list_inserter( Function fun );
        
        // conversion constructor
        template< class Function2, class Arg >
        list_inserter( const list_inserter<Function2,Arg>& );
        
    public:
        template< class U >
        list_inserter& operator,( U u );
        
        template< class U >
        list_inserter& operator=( U u );
        
        // calls 'fun()' with default-constructed object
        list_inserter& operator()();
        
        template< class U >
        list_inserter& operator()( U u );
        
        template< class U, class U2 >
        list_inserter& operator()( U u, U2 u2 )
        {
           //
           // if 'Argument' is 'void'
           //     fun( u, u2 );
           // else
           //     fun( Argument( u, u2 ) );
           //
           return *this;
        }

        //
        // similarly up to 5 arguments
        //
    };
    
    template< class C >
    list_inserter< ... > push_back( C& );
      
    template< class C >
    list_inserter< ... > push_front( C& );

    template< class C >
    list_inserter< ... > insert( C& );

    template< class C >
    list_inserter< ... > push( C& );
      
} // namespace 'assign'
} // namespace 'boost'

請注意,根據 Argument 的類型,operator,()operator()() 的引數如何以不同的方式傳遞給 fun。因此,如果我們只將一個範本引數傳遞給 list_inserter,我們可以轉發函式的「任意」引數列表。如果我們將兩個範本引數傳遞給 list_inserter,我們可以建構具有「任意」建構函式的類型。

而且由於回傳的是 list_inserter 的參考,我們可以以非常節省空間的方式將引數列表鏈接在一起。

函式 make_list_inserter()

一個簡單的 list_inserter「建構函式」函式。此函式的典型用法是以 boost::bind() 的結果來呼叫它,這通常會回傳一些難以理解且奇怪的類別範本。

概要
namespace boost 
{
namespace assign
{  
    template< class Function >
    list_inserter<Function>  make_list_inserter( Function fun )
    {
        return list_inserter<Function>( fun );
    } 
}
}    

自訂引數列表大小

此函式庫使用 boost 預處理器函式庫來實作 operator()()list_of() 的多載版本。預設情況下,您可以使用五個引數呼叫這些函式,但是您也可以在包含此函式庫的標頭之前定義巨集來自訂此數字

#define BOOST_ASSIGN_MAX_PARAMS 10
#include <boost/assign.hpp>


例外和例外安全

函式庫的例外保證與轉發到的函式的保證相同。對於標準容器,這表示為單次插入提供強保證,而為多次插入提供基本保證(前提是複製的物件提供基本保證)。

函式可能會拋出標準例外,例如 std::bad_alloc。但是請注意,不幸的是,標準不保證標準容器中的配置失敗會透過 std::bad_alloc 或衍生自 std::exception 的例外報告。

類別 assignment_exception

例外是由 list_of() 回傳的代理物件中的轉換運算子拋出的。

namespace boost 
{
namespace assign
{
    class assignment_exception : public std::exception
    {
    public:
        explicit assignment_exception( const char* what ); 
        virtual const char* what() const throw();
    };
}   
}  

擴充函式庫

使函式庫與新類別協同工作非常簡單。此程式碼顯示如何將 operator+=() 與容器一起使用

template< class V, class A, class V2 >
inline list_inserter< assign_detail::call_push_back< std::vector<V,A> >, V > 
operator+=( std::vector<V,A>& c, V2 v )
{
    return make_list_inserter( assign_detail::call_push_back< std::vector<V,A> >( c ) )( v );
}
其中 call_push_back 定義為
template< class C >
class call_push_back
{
    C& c_;
public:

    call_push_back( C& c ) : c_( c )
    { }
    
    template< class T >
    void operator()( T r ) 
    {
        c_.push_back( r );
    }
};
請注意,我們將第二個範本引數傳遞給 list_inserter,因此引數列表將用於建構 V 物件。否則,我們可能會嘗試以 n 個引數呼叫 push_back(),而不是一個引數。

另一種方法是結合使用 boost::functionboost::bind()。但是,在這種情況下,必須記住取標準函式庫中函式的位址是非法的。

使用多個引數呼叫函式也非常有用。這個小範例顯示了我們如何利用此功能

//  
// A class representing emails
//
class email
{
public:
    enum address_option
    {
        check_addr_book,
        dont_check_addr_book
    };
    
private:

    typedef std::map< std::string,address_option >  address_map;
    
    //
    // Store list of persons that must be cc'ed
    //
    mutable address_map cc_list;
        
    //
    // This extra function-object will take care of the 
    // insertion for us. It stores a reference to a 
    // map and 'operator()()' does the work. 
    //
    struct add_to_map
    {
        address_map& m;
    
        add_to_map( address_map& m ) : m(m)        
        {}
    
        void operator()( const std::string& name, address_option ao )
        {
            m[ name ] = ao; 
        }
    };

public:
    
    //
    // This function constructs the appropriate 'list_inserter'.
    // Again we could have use 'boost::function', but it is
    // trivial to use a function object.
    //
    // Notice that we do not specify an extra template
    // parameter to 'list_inserter'; this means we forward
    // all parameters directly to the function without 
    // calling any constructor.
    //
    list_inserter< add_to_map >
    add_cc( std::string name, address_option ao )
    {
        //
        // Notice how we pass the arguments 'name' and 'ao' to
        // the 'list_inserter'.
        //
        return make_list_inserter( add_to_map( cc_list ) )( name, ao );
    }
};

//
// Now we can use the class like this:
//
email e;
e.add_cc( "Mr. Foo", email::dont_check_addr_book )
        ( "Mr. Bar", email::check_addr_book )
        ( "Mrs. FooBar", email::check_addr_book );  
完整的範例可以在 email_example.cpp 中看到


範例

其他範例可以在測試檔案中找到


支援的函式庫

以下是已使用 Boost.Assign 測試過的函式庫列表
  1. boost::array
  2. boost::multi_index_container
  3. Boost.Pointer Container


可攜性

函式庫已使用 MVC++ 7.1、GCC 3.2 (在 Cygwin 下) Comeau 4.3.3 成功編譯和測試

在不支援範本轉換運算子的平台上存在已知限制。解決方法是在 list_of() 回傳的物件上呼叫某些成員函式

{
    using namespace std;
    using namespace boost;
    using namespace boost::assign;
    
    vector<int>         v = list_of(1)(2)(3)(4).to_container( v );
    set<int>            s = list_of(1)(2)(3)(4).to_container( s );  
    map<int,int>        m = map_list_of(1,2)(2,3).to_container( m );
    stack<int>         st = list_of(1)(2)(3)(4).to_adapter( st );
    queue<int>          q = list_of(1)(2)(3)(4).to_adapter( q ); 
    array<int,4>        a = list_of(1)(2)(3)(4).to_array( a );
}     

請注意,必須向函式提供引數,以便推斷正確的回傳類型。

某些標準函式庫也已損壞。一個問題是 insert() 可能不起作用

map<int,int> next; 
insert( next )(1,2)(2,3); // compile-time error 
解決方案是改用 map_list_of()
map<int,int> next = map_list_of(1,2)(2,3);


歷史和致謝

用於賦值/初始化函式庫的想法並不是新的。此函式庫的功能與 Leor Zolman 的 STL 容器初始化函式庫非常相似,但是它不依賴字串剖析來實現其目標。

這個函式庫是非侵入式的,對其支援的類別只設定最低限度的要求。重載逗號運算子有時被認為是不好的做法[1]。然而,它在諸如 Generative Matrix Computation Library 和 Blitz 等函式庫中被成功地用來初始化矩陣(請參閱 [2][3])。初始化函式庫以安全的方式重載逗號運算子,透過讓獨立函式返回一個負責初始化的物件來達成。因此,程式設計師需要明確地採取動作才能開始使用重載的 operator,()

最近有一些關於增強語言以支援更好的初始化的討論(請參閱 [4])。

特別感謝以下人士:


參考文獻

  1. Scott. Meyers, "More Effective C++", Item 7, Addison Wesley, 1996
  2. K. Czarnecki 和 U.W. Eisenecker, "Generative programming", Addison-Wesley, 2000
  3. http://www.oonumerics.org/blitz/
  4. Gabriel Dos Reis 和 Bjarne Stroustrup, "Generalized Initializer Lists", 2003


(C) 版權所有 Thorsten Ottosen 2003-2006