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

来源:互联网 发布:win10安装ubuntu 多图 编辑:程序博客网 时间:2024/06/05 13:02

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::function 和boost::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 中初始化矩阵(参见2和3)是成功的先例。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……),只有一个头文件,缺点在于需要借助于正则表达式,编译时还需要导入库文件,会麻烦一些。
原创粉丝点击