Boost C++ 库

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

Assignment 库

版权所有 © 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()() 的代理对象的函数。返回代理对象的函数总是以用于将列表中的值复制到容器中的成员函数命名。所以要用值对填充映射,您可以编写

#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 Preprocessor Library 来实现 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, "广义初始化器列表", 2003


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