Boost C++ 库

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

Boost.Assignment 文档 - Boost C++ 函数库

赋值库

版权所有 © 2003-2006 Thorsten Ottosen

使用、修改和分发受 Boost 软件许可证 1.0 版 (请参阅 https://boost.ac.cn/LICENSE_1_0.txt) 约束。

目录

介绍

operator,() 的实际用途似乎很少。.
Bjarne Stroustrup, C++ 的设计与演进

本库的目的是通过重载 operator,()operator()() 来轻松地用数据填充容器。这两个运算符使得可以构造列表,然后将这些列表复制到容器中。

这些列表在学习、测试和原型制作场景中尤其有用,但其他时候也可能很方便。本库为标准库的容器提供了预定义的运算符,但大部分功能可与任何符合标准的容器一起使用。本库也支持扩展用户定义类型,例如,可以为成员函数调用一个值列表,而不是其正常参数。


教程

您应该能在两分钟内掌握本库的使用。主要组件将在以下几节中进行解释。

前两个函数用于在容器对象创建后添加元素,而后两个函数则用于我们需要初始化对象时。

函数 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 );
} 
请注意,当我们需要使用多个参数(默认最多支持五个参数,但可以 自定义 限制)来构造对象时,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() 的第一个参数决定了匿名列表的类型。对于 stack,匿名列表由 const char* 对象组成,然后转换为 string 对象的 stack。只要存储类型之间的转换是可能的,转换就总是可行的。

请注意,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> 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> list_of() 返回的代理可能抛出的 assignment_exception 类。
<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 的类型,传递给 funoperator,()operator()() 的参数传递方式不同。因此,如果我们只向 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,“Effective C++ 进阶”,项目 7,Addison Wesley,1996
  2. K. Czarnecki 和 U.W. Eisenecker,“生成式编程”,Addison-Wesley,2000
  3. http://www.oonumerics.org/blitz/
  4. Gabriel Dos Reis 和 Bjarne Stroustrup,“通用初始化列表”,2003


(C) Copyright Thorsten Ottosen 2003-2006