The Coder

I am a humble coder.

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  4 随笔 :: 4 文章 :: 9 评论 :: 0 Trackbacks

在调用算法函数的时候,有时候会把不同序列的迭代器当成同一序列使用。
下面提供避免这样错误的一种机制。
本内容来自TCPL(特别版)

有时候,我们会犯这样的错误,把两个不同序列的迭代器去构成一个序列了。比如:

void  f(list < string >&  fruit, list < string >&  citrus)
{
 typedef list
< string > ::const_iterator LI;

 LI p1 
=  find(fruit.begin(), citrus.end(),  " apple " );  //  error, 不在同一序列
 LI p2  =  find(fruit.begin(), fruit.end(),  " apple " );
 LI p3 
=  find(citrus.begin(), citrus.end(),  " pear " );
 LI p4 
=  find(p2, p3,  " peach " );  //  这个更加隐蔽。
}

这里Bjarne Stroustrup给出一个解决问题的途径。其关键就是用整个容器代替x.begin, x.end()的输入。
这样,我们要封装两个东西,1、find()函数。 2、begin(),end()

利用重载,封装find函数。

template < class  In,  class  T >  
In find(Iseq
< In >  r,  const  T &  v)   // 通过重载机制,得到这个find的扩充版本。
{
    
return  find(r.first, r.second, v);  // 标准库中的find
};

 

利用对偶,封装迭代器。
首先,我们构造一个Iseq以保证迭代器是统一序列成对输入的。

template < class  In > struct  Iseq:  public  pair < In, In > {
    Iseq(In i1, In i2): pair
< In, In > (i1, i2){}
};

 

接着构造一个协助函数,直接传递容器。

template < class  C > Iseq < typename C::iterator >  iiseq(C &  c)  // C为容器
{
    
return  Iseq < typename C::iterator > (c.begin(), c.end());
}


这样,我们可以利用上面的机制,来避免所提出的错误。

void  f(list < string >&  fruit, list < string >&  citrus)
{
 typedef list
< string > ::const_iterator LI;

 LI p1 
=  find(iiseq(fruit),  " apple " ); 
 LI p2 
=  find(iiseq(citrus),  " apple " );
 LI p3 
=  find(citrus.begin(), citrus.end(),  " pear " );  //
}

 

下面我们仔细分析整个机制的几个细节。
先让我们来看看pair的样子。

template < class  T1,  class  T2 > struct  std::pair{  // 这里用struct来定义
    typedef T1 first_type;
    typedef T2 second_type;

    T1 first;
    T2 second;

    pair(): first(T1()), second(T2()){}
    pair(
const  T1 &  x,  const  T2 &  y): first(x), second(y){}
    template
< class  U,  class  v >
        pair(
const  pair < U, V >&  p): first(p.first), second(p.second){}
    
//
};

注意pair的两个数据成员first, second都是public的,所以Iseq继承pair之后可以直接访问。

考察find()函数的重载版本
find(Iseq<In> r, const T& v)
注意“Iseq<In> r”使用值传递,而不用引用传递(Iseq<In>& r)。
这是因为iiseq协助函数返回一个临时对象,所以在find中,不能用引用传递。
template<class C>Iseq<typename C::iterator> iiseq(C& c) //C为容器
{
 return Iseq<typename C::iterator>(c.begin(), c.end());
}
大家可能会考虑到效率问题,觉得值传递可能不妥。其实不然,我们可以发现,Iseq里面的数据成员是两个Iterator,一般来说不是很大(有时,就是两个指针),在效率上不会产生很大的影响。

还有这里代码中出现typename,(如return Iseq<typename C::iterator>(c.begin(), c.end());) 可能对初学者来说有些生疏。为什么不直接写: Iseq<C::iterator>(c.begin(), c.end())。这是由于编译器不能直接认出C::iterator是一种类型,所以我们加上修饰符号typename告诉编译器C::iterator使用一种类型。


 

posted on 2006-06-02 22:25 TH 阅读(198) 评论(0)  编辑 收藏 引用 所属分类: STL和标准函数库

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理