引言:我希望看我的文章的朋友都能够去把我的代码编译一次,这样会发现理解我的文章很轻松。我给的代码都是直接无需修改就可编译并运行的。
前一篇文章http://blog.csdn.net/zy498420/archive/2010/11/10/6000129.aspx说到的种子皮在vc6的不完美适用问题,被彻底的解决了。剃了个头恰好就有思路了。
解决的方法很简单,让前一篇文章所说的bug在且仅在vc6中出现,完美。以毒攻毒,以bug1解决bug2!!
原理:vc6重载决议能力有限,但是却允许右值(临时值)传递给非const引用参数。当然同等条件它的重载决议优先选择带const引用参数(这个规则按标准原本只能用在左值上)的函数。
template <typename D, typename L, typename R, typename C>
inline const _et_private::binary_tree_ref<D, _et_private::binary_tree_ref<D, L, R>, C >
operator + (const _et_private::binary_tree_ref<D, L, R>& lhs, const C& rhs){
return _et_private::binary_tree_ref<D, _et_private::binary_tree_ref<D, L, R>, C > ( lhs, rhs);
}
template <typename D, typename L, typename R, typename C>
inline const _et_private::binary_tree_ref<D, C, _et_private::binary_tree_ref<D, L, R> >
operator + (const C& lhs, const _et_private::binary_tree_ref<D, L, R>& rhs){
return _et_private::binary_tree_ref<D, C, _et_private::binary_tree_ref<D, L, R> > (lhs, rhs);
}
瞧,如果C类型也是binary_tree_ref时,编译器就不知道如何2选1了。符合标准的编译器支持对这种情况进行特化来帮助选择,然而vc6对于特化后的函数并没有提升它的重载优先级,而是像二师兄那样傻乎乎的问:又来了一个,3选1,选哪个呢?如果我是观音,我也想踢他了。
解决:把第2个函数的第2个参数去掉const 修饰
template <typename D, typename L, typename R, typename C>
inline const _et_private::binary_tree_ref<D, C, _et_private::binary_tree_ref<D, L, R> >
operator + (const C& lhs, _et_private::binary_tree_ref<D, L, R>& rhs){
return _et_private::binary_tree_ref<D, C, _et_private::binary_tree_ref<D, L, R> > (lhs, rhs);
}
此时C若不是binary_tree_ref,就选择这个新函数(根据标准,把右值(临时值)传递给非const引用参数,编译器会抱错gcc。icc居然只给出警告,不完全算错的原因在于这个可能非法的引用我们永远不去解引用!!所以持有一个可能非法的引用并不会影响程序的运行,这也是这里vc6的“违法”行为居然没有任何坏效果的原因。不过icc是真聪明(真正的分析了inline函数内部的情况发现这个错误不会被触发),gcc是死守规矩的良民,而vc6是纯粹的瞎猫遇到了死老鼠。)。
如果C是binary_tree_ref,根据const永远优先的重载决议原则,vc6自动去选择第一个。
其实选择哪一个函数都是一样的,我们这里需要做的就是帮编译器做出选择。向左转还是向右转,这不是一个问题,只要你勇敢的迈出你的脚步。
好了,上完整代码。种皮和播种2个方法一起贴了上来,供大家参考,种皮和播种2个方法经过了非常多的测试,2者之间效率几乎没有任何区别。没有任何一个编译器能够对其中一种方法能多做一些优化。播种这一招的保留我始终觉得是有意义的。另外我对播种干脆做一个限制:必须放在最左端,不再允许放在第1和第2之间。有些接口,简单些更好用,太通用反而让人郁闷,守规矩总是一件好事。现在胡乱播种有可能编译器会报错!