Assignment Library賦值庫

Copyright c 2003-2006 Thorsten Ottosen

Use, modification and distribution is subject to the Boost Software License, Version 1.0 (see http://www.boost.org/LICENSE_1_0.txt).

Table of Contents目錄


Introduction簡介

There appear to be few practical uses of operator,().
實際上,很少會使用operator, ()。

Bjarne Stroustrup, The Design and Evolution of C++

本庫的目的是通過重載operator,()operator()()使 得將數據填入容器中可以更容易。這兩個操作符讓我們可以構造一列數據然後把它們拷貝進容器中:

這些列表對於學習、測試和原型化顯然很有用,也很容易得到。本庫為標準庫中的容器預定義了這些操作符,但與一些符合標準的容器一起工作 會有更多的功能。本庫同時也可以擴展至用戶定義類型,例如,用一列數值來調用成員函數,替代普通的參數。


Tutorial指南

只需兩分鐘你就可以使用這個庫了。本節會解釋各個主要元件:

頭兩個函數用於在一個已創建的容器後增加元素,接下來的兩個函數用於初始化一個對象。

函數 operator+=()

用 operator+=() 來向一個 vector (或其它標準容器) 填入值,你可以這樣寫:

#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()()。這個返回代理對象的函數總是以用來將值插入容 器的那個成員函數來命名。因此,要將值對填入一個 map,你可以這樣寫:

#include <boost/assign/list_inserter.hpp> // for 'insert()'
#include <boost/assert.hpp>
#include <string>
using namespace std;
using namespace boost::assign; // bring 'insert()' into scope

{
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 );
}
注意,當我們需要用多個參數(缺省支持最多五個參數,但這個限制是可以 customized定制的)構造對像時,也可以使用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(1)(2)(3)(5)(7)(11);
BOOST_ASSERT( primes.size() == 6 );
BOOST_ASSERT( primes.back() == 11 );
BOOST_ASSERT( primes.front() == 1 );

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>, 請見 supported libraries支持的庫 中的列表。

注意,由 list_of() (及其變體)所返回的類型具有重載的比較操作符。這樣你就可以編寫象 BOOST_CHECK_EQUAL( my_container, list_of(2)(3)(4)(5) ); 這樣的測試代碼。

函數 map_list_of()

這個函數單純是為了更方便地使用map而定義的。它的用法示例如下:
#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, 則可以用 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()

一個 "複雜的" 例子

作為最後一個例子,假設我們需要記錄足球比賽的結果。一個隊 如果贏則記1分,否則記0分。如果每組已經打了三場比賽,則代碼如下所示:

#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(),參數 tuple (arg1,arg2,...,argN) 中的第一個參數 arg1 用 於構造一個鍵;這意味著第一個參數只要求可以轉換為容器的 key_type 即可。剩下的參數則用於構造整個 mapped 對象。

函數 ptr_list_of()

就像可以使用 list_of() 來初始化容器一樣,你也可以用 ptr_list_of() 來初始化一個 pointer container指針容器。 以下是一個小例子:
#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 ).to_container( deq );
BOOST_CHECK( deq.size() == 5 );
}
留意其中的 .to_container(deq) 是用於幫助多數編譯器實現轉換(只有少數編譯器可以正確處理)。還要注意的是指針 map 不被支持。

這就是所有的東西了,現在你已經準備好使用這個庫了。


Reference參考

我們應該留意庫的實現方法。利用一個自由函數(push_back()operator+=()) 返回一個代理對象,由該對像負責插入或賦值操作。這個代理對像通過重載 operator,()operator()() 來實現插入或賦值,並從這些操作符函數里調用 "insert" 函數。這個 "insert" 函數實際上是使用 boost::function 保存在代理對像內部的。

通常重載 operator,() 不是個好主意,因為它會引起令人意外的結果,不過在本庫中這種方法是安全的,因為用戶永遠不會直接操作重載了 operator,() 的對象。無論如何,你應該知道:

在以逗號分隔的列表中的表達式並不跟隨C++內建的逗號操作符的規則。這意味著在以逗號分隔的列表中,各個表達式的 求值順序是未定義的,正如在函數參數列表中一樣。

本文檔中大多數示例代碼都使用 int,當然對於所有可複製構造的任意類型都是可用的。被插入的數據不一定 要是常量數據,也可以是變量或從函數返回的數據;唯一的要求是,這些數據的類型要可以轉換為保存在容器中的數據類型。

通過const引用來傳遞對象的改進已經完成。最開始的時候參數是以值的方式傳遞的(現在的 tuple_list_of() 仍是如此)。要記住的一件事就是,可以通過 boost::ref 來傳遞引用。

所有東西都置於名字空間 boost::assign 之下。

以下是更多的細節:

Headers頭文件

以下給出本庫的頭文件的一個概覽。請留意 <boost/assign/list_inserter.hpp> 會包含在每個定義了 operator+=() 頭文件中。

Header頭文件 Includes包含
<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::deque的operator+=(), <deque>
<boost/assign/std/list.hpp> std::list的operator+=(), <list>
<boost/assign/std/map.hpp> std::mapstd::multimap 的 operator+=(), <map>
<boost/assign/std/queue.hpp> std::queuestd::priority_queue 的 operator+=(), <queue>
<boost/assign/std/set.hpp> std::setstd::multiset 的 operator+=(), <set>
<boost/assign/std/slist.hpp> std::slistoperator+=(), <slist> (如果有slist)
<boost/assign/std/stack.hpp> std::stack 的 operator+=(), <stack>
<boost/assign/std/vector.hpp> std::vector 的 operator+=(), <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()

Standard containers標準容器

以下摘要中的省略號(...)代表「實現定義」。operator+=() 返回一個代理,該代理根據容器所支持的操作前轉調用 push_back(),insert(), 或 push()。

Synopsis摘要

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> 的匿名列表。這兩個函數所返回的對象保證具有以下接口。

Synopsis摘要
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 );

// 插入缺省構造的對象
Implementation-defined& operator()();

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

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

//
// 以此類推至5個參數
//

//
// 轉換至一個'容器'。'容器'必須具有一個帶兩個迭代器參數的構造函數
//
template< class Container >
operator Container() const;

//
// 轉換至一個容器適配器,如 'std::stack<>'.
//
Convertible-to-adapter to_adapter() const;

//
//
// 轉換至 'boost::array<T,std::size_t>'. 如果被賦值的變量太小,
// 將拋出一個 assignment_exception
// 如果被賦值的變量太大,剩餘的部分將被缺省構造。
//
template< template <class,std::size_t> class Array, class U, std::size_t sz >
operator Array<U,sz>() const;
};

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

這個類負責插入元素到容器中,它是擴展本庫以支持你想要的類的關鍵所在。

Synopsis摘要

namespace boost
{
namespace assign
{
template< Function, Argument = void >
class list_inserter
{
Function fun;

public:
explicit list_inserter( Function fun );

// 轉換構造函數
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 );

// 以缺省構造的對象調用 'fun()'
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;
}

//
// 以此類推至5個參數
//
};
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'

請注意,operator,()operator()() 的參數傳遞給 fun 的方式根據 Argument 的類型而有所不同。所以,如果我們僅指定一個模板參數給 list_inserter,我們可以前 轉 "任意" 的函數參數列表。如果我們指定兩個模板參數給 list_inserter,我們就 可以用 "任意" 的構造函數來構造類型。

此外,由於返回的是一個 list_inserter 引用,因此我們可以將參數列表串在一起,這是很節省空間的方式。

函數 make_list_inserter()

這是構造一個 list_inserter 的簡便的 "構造器" 函數。這個函數的一種典型用法就是,以 boost::bind() 所返回的結果來調用它,因為通常這個結果都是一些很難讀懂和非常怪異的類模板。

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

Customizing argument list sizes定制參數列表大小

本庫使用了 boost Preprocessor Library預處理庫來實現 operator()()list_of() 的重載版本。缺省情況下你可以用最多五個參數來調用這些函數,不過你也可以改變這個限制,方法就是,在包含本庫的頭文件之前定義一個宏,如下所示:

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


Exceptions and exception-safety異常及異常安全

本庫的異常保證與前轉至的函數的異常保證一致。對於標準容器而言,單個元素的插入操作是強異常安全的,而多個元素的插入操作則是基本異 常安全的(假設被複製的對象給予了基本異常安全的保證)。

這些函數可能拋出標準異常,如 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();
};
}
}

Extending the library擴展本庫

要讓本庫與新的類一起使用非常簡單。以下代碼示範了如何將 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() 而不是1個了。

另一種方法是,結合使用 boost::functionboost:: bind()。但是,這時你要記住,對標準庫中函數取地址是非法的。

用多個參數來調用函數也非常有用。下面這個小例子示範了如何利用這個功能:

// 
// 一個表示email的類
//

class email
{
public:
enum address_option
{
check_addr_book,
dont_check_addr_book
};

private:

typedef std::map< std::string,address_option > address_map;

//
// 保存要cc的人員列表
//
mutable address_map cc_list;

//
// 這個函數對像負責為我們進行插入操作。它保存了一個map的引用
// 以及 'operator()()' 來完成這項工作。
//
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:

//
// 這個函數構造了適用的 'list_inserter'。我們也可以使用
//
'boost::function', 但通常會用函數對象。
//
// 注意,我們沒有為 'list_inserter' 指定另一個模板參數;
// 這意味著我們直接將所有參數前轉至函數而不調用任何構造函數。
//
list_inserter< add_to_map >
add_cc( std::string name, address_option ao )
{
//
// 請注意我們是如何把參數 'name' 和 'ao' 傳遞給 'list_inserter' 的。
//
return make_list_inserter( add_to_map( cc_list ) )( name, ao );
}
};

//
// 現在我們可以這樣使用這個類:
//

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


Examples例子

可以從以下測試文件中找到其它例子:


Supported libraries支持的庫

以下列出的是已經與 Boost.Assign 測試過的庫:
  1. boost::array
  2. boost::multi_index_container
  3. Boost.Pointer Container


Portability可移植性

本庫已經用 MVC++ 7.1, GCC 3.2 (under 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); // 編譯期錯誤
解決的辦法是,使用 map_list_of() 來代替:
map<int,int> next = map_list_of(1,2)(2,3);


History and Acknowledgment歷史和感謝

(不譯)The idea for an assignment/initialization library is not new. The functionality of this library resembles Leor Zolman's STL Container Initialization Library a great deal, but it does not rely on string parsing to achieve its goals.

The library is non-intrusive and puts only a minimum of requirements on its supported classes. Overloading operator comma is sometimes viewed as a bad practice [1]. However, it has been done with success in eg. the Generative Matrix Computation Library and Blitz to initialize matrices (see [2]) and [3]). The Initialization Library overloads the comma operator in a safe manner by letting free standing functions return an object that is responsible for the initialization. Therefore it takes explicit action from the programmer to begin using the overloaded operator,().

There has recently been some discussion about enhancing the language to support better initialization (see [4]).

Special thanks goes to


References參考

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


(C) Copyright Thorsten Ottosen 2003-2006