正文

boost::assign让容器赋值更简单2008-02-05 16:01:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/rickone/32544.html

分享到:

分析源码其实很有意思,尤其是优秀的源码。boost::assign库,老实说,设计初衷有些无聊,它无非是想让容器的赋值(插入)操作更加简单,简单得就像脚本语言一样,比如,原来的STL里的容器有各种各样的,不同的容器会有不同的赋值方式,比如:

#include<vector>
using namespace std:
int main()
{
   vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  return 0;
}

如果你不觉得敲‘.push_back’多么麻烦,大可不必管这个库,实际上这样写,机器看起来更简单。而boost就想办法让人偷懒,而机器转一些弯,成了这样:

int main()
{
  vector<int> v;
  v+=1,2,3;
  return 0;
}

能够运行的示范代码我没有找到啊,只是写个样子出来,OMG,真像脚本语言!你要问,效率如何?可能会牺牲一些,就像,我问你,仿函数比函数的调用效率是不是低一些,如果是,那STL里还不是大量用仿函数?但这些差别,绝对不是相差很大,不会有质的差别。

好了,下面进行分析,我不是设计者,不知道大哥他是怎么想的,我只从分析的角度说明。

1,设计各种仿函数来管理具体的容器和它的赋值方法

仿函数比函数好的一个地方,它可以有成员变量,而这里就关联了这个具体的容器。在源码的assign/list_inserter.hpp里的前面部分,有一个namespace assign_detail命名空间,在那里定义了各种,仿函数,比如调用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 );
        }
    };

调用insert的

    template< class C >
    class call_insert
    {
        C& c_;
    public:
        call_insert( C& c ) : c_( c )
        { }
   
        template< class T >
        void operator()( T r )
        {
            c_.insert( r );
        }
    };

那个大C表示容器Container,看到成员c了没有,在那里关联了外面的具体容器,它是一个引用,所以没有进行低效的复制操作,只是一个关联。operator()是调用函数,它简单的指向不同容器的具体赋值操作。

2,重载各种符号运算

再下来,外面是一个封装,类list_inserter,它是一个核心类,它关联一个具体的赋值仿函数对象,同时,重载了一些简单的操作符,比如operator, operator()等。一但成功建立了一个list_inserter,后面的赋值就可以用一些简单的符号进行了。它的样子是这样的:

template< class Function, class Argument = assign_detail::forward_n_arguments >
    class list_inserter
   {
        ...
    private:
       
        list_inserter& operator=( const list_inserter& );
        Function insert_;
   }

它关联一个赋值仿函数对象,具体的操作由它来完成,然后你会看到,各种各样的操作,比如operator,都是用insert_()来完成的,那个拷贝函数设成私有,就是用来禁止拷贝的,如果用户拷贝会有一个权限错误,不能访问私有成员,如果类本身访问,会有一个连接错误,总之是不能拷贝,当然那个insert_也不能,同样,具体的容器也就没有拷贝。

list_inserter是怎么建立起来的,它需要一些外部的函数来完成,这些函数是和具体的容器相关的,所以需要为每种容器写一份,在assign/std/目录里就是各种这样的定义,如vector.hpp里有简单这样几行:

namespace boost
{
namespace assign
{

    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 push_back( c )( v );
    }
   
}
}

(只例关键的),那是一个重载的operator+=,左边是一个V类型A分配器的vector,右边是一个V2类型的值,它具体是由push_back函数完成,你看到它这样写push_back(c)(v),实际上是先调用了push_back(c),它返回了一个list_inserter,再后面的(v),是将v插入进去,看push_back()定义:

    template< class C >
    inline list_inserter< assign_detail::call_push_back<C>,
                          BOOST_DEDUCED_TYPENAME C::value_type >
    push_back( C& c )
    {
        static BOOST_DEDUCED_TYPENAME C::value_type* p = 0;
        return make_list_inserter( assign_detail::call_push_back<C>( c ),
                                   p );
    }

它果然返回的是一个list_inserter,插入仿函数是call_push_back,每二个参数是容器元素类型,每个容器内都要定义一个value_type,那个BOOST_DEDUCED_TYPENAME我猜就是typename,说明一个内嵌类型,那个static定义,也没有为多次调用push_back带来任何负担,再转调用make_list_inserter(),看它的定义:

    template< class Function, class Argument >
    inline list_inserter<Function,Argument>
    make_list_inserter( Function fun, Argument* )
    {
        return list_inserter<Function,Argument>( fun );
    }

它有两个形式,这只是其中一个,有元素类型的定义,那个传进来的Argument* p没有任何用,只是做一个类型推导,用仿函数对象fun初始化list_inserter,看到这里没有引用返回,list_inserter只是不能拷贝,但是可以拷贝构造。所以push_back( c )最终得到的、具现出的结果是:list_inserter<assign_detail::call_push_back<vector<int>>,int>(
  assign_detail::call_push_back<vector<int>>(v) )

再调用list_inserter的operator(),它转调用的是insert_(),即用assign_detail::call_push_back<vector<int>>的operator()将初值插入到了容器。

总的看一下list_inserter支持多少种操作符重载(下面都是list_inserter的成员函数):

        list_inserter& operator()()
        {
            insert_( Argument() );
            return *this;
        }
       
        template< class T >
        list_inserter& operator=( const T& r )
        {
            insert_( r );
            return *this;
        }
       
        template< class T >
        list_inserter& operator=( assign_detail::repeater<T> r )
        {
            return operator,( r );
        }
       
        template< class Nullary_function >
        list_inserter& operator=( const assign_detail::fun_repeater<Nullary_function>& r )
        {
            return operator,( r );
        }
       
        template< class T >
        list_inserter& operator,( const T& r )
        {
            insert_( r  );
            return *this;
        }

        template< class T >
        list_inserter& operator,( const assign_detail::repeater<T> & r )
        {
            return repeat( r.sz, r.val );
        }

        template< class Nullary_function >
        list_inserter& operator,( const assign_detail::fun_repeater<Nullary_function>& r )
        {
            return repeat_fun( r.sz, r.val );
        }

        template< class T >
        list_inserter& repeat( std::size_t sz, T r )
        {
            std::size_t i = 0;
            while( i++ != sz )
                insert_( r );
            return *this;
        }
       
        template< class Nullary_function >
        list_inserter& repeat_fun( std::size_t sz, Nullary_function fun )
        {
            std::size_t i = 0;
            while( i++ != sz )
                insert_( fun() );
            return *this;
        }

        template< class SinglePassIterator >
        list_inserter& range( SinglePassIterator first,
                              SinglePassIterator last )
        {
            for( ; first != last; ++first )
                insert_( *first );
            return *this;
        }
       
        template< class SinglePassRange >
        list_inserter& range( const SinglePassRange& r )
        {
            return range( boost::begin(r), boost::end(r) );
        }
       
        template< class T >
        list_inserter& operator()( const T& t )
        {
            insert_( t );
            return *this;
        }

主要是operator,,它完成插入,还有operator()(T&),另外operator()()是插入一个临时元素,相当于空一位,此外还有几个帮助函数,assign_detail::repeater<T>和assign_detail::fun_repeater<Fun>,它们的作用很简单,可以帮助插入多个相同元素,或用一个函数构造多个元素,我们可以使用namespace assign里的repeat和repeat_fun来完成。

另外,还有一个mpl里的东西,那个if_c,其实可以找到他的定义,因为mpl库很大,不好说太多,那句是这样的:

typedef BOOST_DEDUCED_TYPENAME mpl::if_c< is_same<Argument,assign_detail::forward_n_arguments>::value,
                                                  n_arg_type,
                                                  single_arg_type >::type arg_type; 

is_same<>是type_traits库里的,用来测试那两个类型是否相同,如果相同value==true,否则value==false,而if_c里定义的type,对第一个参数有特化版,如果true则type==第二个参数,否则type==第三个参数,它就像我们以前的if-else一样,只不过是在编译期做的判断。

最后,马上要过年啦,2008新春快乐!

rickone 2008/2/5

阅读(5797) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册