C++ Boost Assign 文档(翻译) (二)

o 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'

  请注意operator,() and operator()() 的参数是如何以不同的方式传递给fun()的,fun()依赖于Argument()的类型。因此如果我们仅仅传递一个模板参数给list_inserter,我们可以传送函数的"任意"参数列表。如果我们传递两个模板参数,我们可以用"任意"构造器构造类型。

  因为返回的是一个指向list_inserter的引用,所以我们连接参数列表的方式空间效率很高。

o 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 );
                } 
        }
}

o 定制参数列表的长度

  这个库用boost.Preprocessor执行重载的operator()() and list_of()。缺省情况下,你可以使用5个参数,但是你可以通过在库的头文件中定义宏来改变参数的个数:

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

4、异常和异常安全

  对异常的保证和原有函数相同。 对于标准容器,这意味着对单向插入方式的强壮保证和对多种插入方式的基本保证(也提供给被拷贝对象以基本保证)。

  这些函数可以抛出类似于std::bad_alloc的标准异常。但不幸的是,标准并不保证在标准容器中内存分配失败可以被std::bad_alloc或是继承自std::exception的异常报告。

assignment_exception 类

  这个异常被代理对象中转换操作符(the conversion operator)抛出,这个代理对象就是被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();
                };
        }   
}

5、扩展库

  让库支持新的容器是非常简单的,以下代码示范了如何在一个容器中使用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。

6、样例

  附带的例子可以在下列测试文件中找到:

  • email_example.cpp my_vector_example.cpp
  • multi_index_container.cpp
  • array.cpp
  • list_of.cpp
  • std.cpp
  • list_inserter.cpp
  • list_of_work_around.cpp

7、支持的库

  以下库中的容器经过测试可以被assign直接支持:

  1. boost::array
  2. boost::multi_index_container

8、可移植性

  assign库在以下编译器通过测试可以被成功编译:Microsoft VC++ 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);

9、历史 致谢

  赋值与初始化库并不是一个新鲜玩意儿。库的功能非常类似于Leor Zolman的STL Container Initialization Library, 不同的是它并不依赖于字符解析达到目标。

  这个库是非侵入性的,对于所支持的容器,尽可能小的增加了前提条件。重载operator,()被认为是一个坏的尝试1。但是,在the Generative Matrix Computation Library 和 Blitz 中初始化矩阵(参见 23)是成功的先例。assign通过让独立函数返回一个负责初始化的对象,以一种安全的方式重载了operator,()。它采用显式的方式避免了程序员使用operator,().

  最近有一些关于使C++支持更好的初始化方式的讨论。(参见4)。

特别感谢

  • Leor Zolman 参与了许多有决定意义的讨论。
  • Tom Brinkman for being review manager。
  • Joaquín Muñoz 将库移植到 vc6/vc7 所做的工作。
  • Pavel Vozenilek 提供了数不清的建议, 改进和可移植性的修正。

10、参考书目

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

Copyright © Thorsten Ottosen 2003-2004


注1 Leor Zolman的STL Container Initialization Library通过字符解析达到分解参数的目的,但是只支持push_back与insert(关联容器)两种行为,所以slist、stack、deque都无法支持,且对值类型有些挑剔(eg. 不支持pair)。译者曾借助于型别推导改进使之支持到所有容器。它的好处在于简单的函数接口(make_cont,set_cont,app_cont,以及make_cont_p……),只有一个头文件,缺点在于需要借助于正则表达式,编译时还需要导入库文件,会麻烦一些。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值