boost源码分析之 BOOST_FOREACH(2)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Lunar_lty/article/details/24329727

现在,我们已经可以保证表达式最终只会被运算一次,但马上问题又来了,考虑一下这样的代码:

FOREACH( char ch, string("Hello") )
{
   cout << ch;
}

代码的本意是打印"Hello"里面的每一个字符串,但并非如此,因为string("Hello")是一个临时对象,也是一个rvalue,把FOREACH宏展开一点来看就很明显了:

auto_any_base const & iter = begin( string("Hello") );

begin()函数将返回一个迭代器,它指向一个临时对象。。。可以考虑针对该临时对象copy出一个新对象出来,新对象的生命周期足够长,以保证迭代器不会失效。当然,拷贝一个容器代价是比较高的,除非真的有必要,我们才会这么做,当container是一个lvalue的时候,可以直接使用;只有当container是一个rvalue的时候才需要进行拷贝。

假设已经有某个函数可以将区分container是lvalue or rvalue,并且用一个bool变量保存在is_rvalue中,可以构造一个辅助函数,输入参数是container,is_rvalue,根据需要,当is_rvalue为true时,返回一个拷贝,为false时,返回一个指针,boost::variant可以满足此要求,代码看起来是这样子的:

// contain() - returns a Container or a Container*
template< class Container >
auto_any< boost::variant<Container const*,Container> >
contain( Container const & t, bool const & is_rvalue )
{
   // Return either a Container or a Container* depending on whether
   // the container is an rvalue or not.
   typedef boost::variant<Container const*,Container> variant_t;
   return is_rvalue ? variant_t(t) : variant_t(&t);
}

begin()函数也应作相应的修改:

// begin() - returns the begin iterator
template< class Container >
auto_any< typename Container::const_iterator >
begin( auto_any_base const & container, bool is_rvalue,type2type<Container> )
{
   typedef boost::variant<Container const*,Container> variant_t;
   variant_t & var = auto_any_cast< variant_t>(container);
   // Extract either a Container or a Container* depending on whether
   // the container is an rvalue or not.
   Container const & c = is_rvalue ? boost::get<Container>(var)
                                   : *boost::get<Container const *>(var);
   return c.begin();
}

假设之前判断container是否为rvalue为一个宏EVAL(container, is_rvalue),则FOREACH宏定义如下:

#define FOREACH( item, container ) \
   bool is_rvalue; \
   auto_any_base const & cont = contain(EVAL(container,is_rvalue), is_rvalue ); \
   auto_any_base const & iter = begin( cont, is_rvalue, ENCODED_TYPEOF(container) ); \
   ...

剩下的问题就是,如何实现EVAL呢,FOREACH作者是这样来实现的:

struct rvalue_probe
{
   template< class T >
   rvalue_probe( T const & t, bool & b )
        : ptemp( const_cast< T * >( &t ) ), is_rvalue( b )
   {}

   template< class R > operator R()
   {
        is_rvalue = true;
        return *static_cast< R * >( ptemp );
   }

   template< class L > operator L &() const
   {
        is_rvalue = false;
        return *static_cast< L * >( ptemp );
   }

   void * ptemp;
   bool & is_rvalue;
};

#define EVAL( container, is_rvalue )    \
          ( true ? rvalue_probe( (container), is_rvalue ) : (container) )

rvalue_probe和container作为条件运算符的两个操作数,它们的类型必须相等,或者经过转换之后必须相等。如果container是一个lvalue,则匹配operator L&() const,如果container是一个rvalue,则匹配 operator R(),这点和函数重载解析很类似。

到此为止,FOREACH宏的两个难点已经得到解决:

扫描二维码关注公众号,回复: 5491115 查看本文章

1、不计算表达式就可以解析出表达式类型

2、判断一个表达式是否为rvalue

而这两个问题都是通过条件运算符解决!


参考资料:

1. Conditional Love: FOREACH Redux by Eric Niebler February 17, 2005 http://www.artima.com/cppsource/foreach.html


猜你喜欢

转载自blog.csdn.net/Lunar_lty/article/details/24329727