分析源码其实很有意思,尤其是优秀的源码。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
评论