Boost C++ 库

...世界上最受尊敬和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

赋值库

版权所有 © 2003-2006 Thorsten Ottosen

使用、修改和分发受 Boost 软件许可 1.0 版的约束(参见 https://boost.ac.cn/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()()。返回代理对象的函数始终以用于将列表中的值复制到容器中的成员函数命名。因此,要用值对填充 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 ); } 
请注意,当我们需要使用多个参数构造对象时,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()

定义此函数纯粹是为了在处理 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_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) 以帮助许多糟糕的编译器弄清楚转换(少数几个可以正确理解)。另请注意,不支持指针 map。

就是这些;现在您可以开始使用这个库了。


参考

值得注意的是库的实现方式。一个独立的函数(例如 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> operator+=() 用于 std::deque, <deque>
<boost/assign/std/list.hpp> operator+=() 用于 std::list, <list>
<boost/assign/std/map.hpp> operator+=() 用于 std::mapstd::multimap , <map>
<boost/assign/std/queue.hpp> operator+=() 用于 std::queuestd::priority_queue, <queue>
<boost/assign/std/set.hpp> operator+=() 用于 std::setstd::multiset, <set>
<boost/assign/std/slist.hpp> operator+=() 用于 std::slist (如果该类可用), <slist>
<boost/assign/std/stack.hpp> operator+=() 用于 std::stack, <stack>
<boost/assign/std/vector.hpp> operator+=() 用于 std::vector, <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]。但是,在例如生成矩阵计算库和 Blitz 中,它已成功用于初始化矩阵(参见 [2])和 [3])。初始化库通过让独立的函数返回负责初始化的对象,以安全的方式重载逗号运算符。因此,程序员需要明确的操作才能开始使用重载的 operator,()

最近有一些关于增强语言以支持更好初始化的讨论(参见 [4])。

特别感谢


参考文献

  1. Scott. Meyers, "More Effective C++", 第 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