版权声明:本文为博主原创文章,未经博主允许不得转载。 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宏的两个难点已经得到解决:
1、不计算表达式就可以解析出表达式类型
2、判断一个表达式是否为rvalue
而这两个问题都是通过条件运算符解决!
参考资料:
1. Conditional Love: FOREACH Redux by Eric Niebler February 17, 2005 http://www.artima.com/cppsource/foreach.html