Perfect forwarding!!
とりあえずオレツエー状態。
あとdiffはどっかに貼るべきだったかな・・・
で、
GCC4.4.4-14ubuntu5
GCC4.5.2
GCC4.6.0 20101223 (experimental)
Clang2.9(svn118883)
で確認。
多分BorlandとかmsvcだとSFINAEの挙動が若干変わって悲しいことになりそう。
Index: boost/move/move.hpp =================================================================== --- boost/move/move.hpp (revision 67436) +++ boost/move/move.hpp (working copy) @@ -27,10 +27,19 @@ #include <memory> //uninitialized_copy #include <iterator> //std::iterator #include <boost/mpl/if.hpp> +#include <boost/mpl/not.hpp> +#include <boost/mpl/or.hpp> +#include <boost/mpl/and.hpp> #include <boost/utility/enable_if.hpp> #include <boost/mpl/bool.hpp> #include <boost/type_traits/has_trivial_destructor.hpp> +#include <boost/type_traits/remove_cv.hpp> +#include <boost/type_traits/remove_reference.hpp> +#include <boost/type_traits/is_lvalue_reference.hpp> +#include <boost/type_traits/is_const.hpp> +#include <boost/type_traits/is_volatile.hpp> #include <boost/utility/addressof.hpp> +#include <boost/static_assert.hpp> //! Defining or undefining this macro will change Boost.Move behaviour //! for copyable and movable classes when assigning from non-const rvalues: @@ -130,20 +139,82 @@ template <class T> struct is_rv + : public ::boost::mpl::bool_<false> { - static const bool value = false; }; template <class T> struct is_rv< rv<T> > + : public ::boost::mpl::bool_<true> { - static const bool value = true; }; } //namespace move_detail { ////////////////////////////////////////////////////////////////////////////// // +// move_detail::is_rv_ref +// +////////////////////////////////////////////////////////////////////////////// + +namespace move_detail +{ + +template < typename T > +struct is_rv_ref + : public mpl::and_< + is_rv< typename remove_cv< + typename remove_reference< T >::type >::type >, + is_lvalue_reference< T > >::type {}; + +} // namespace move_detail + +////////////////////////////////////////////////////////////////////////////// +// +// move_detail::add_rv_ref +// +////////////////////////////////////////////////////////////////////////////// + +namespace move_detail +{ + +template < + typename T, + bool = is_const< T >::value, + bool = is_volatile< T >::value > +struct add_rv_ref; + +template < typename T > +struct add_rv_ref< T, false, false > +{ + typedef ::BOOST_MOVE_NAMESPACE::rv< T > & type; +}; + +template < typename T > +struct add_rv_ref< T, false, true > +{ + typedef typename remove_cv< T >::type Ty_; + typedef volatile ::BOOST_MOVE_NAMESPACE::rv< Ty_ > & type; +}; + +template < typename T > +struct add_rv_ref< T, true, false > +{ + typedef typename remove_cv< T >::type Ty_; + typedef const ::BOOST_MOVE_NAMESPACE::rv< Ty_ > & type; +}; + +template < typename T > +struct add_rv_ref< T, true, true > +{ + typedef typename remove_cv< T >::type Ty_; + typedef const volatile ::BOOST_MOVE_NAMESPACE::rv< Ty_ > & type; +}; + +} // namespace move_detail + +////////////////////////////////////////////////////////////////////////////// +// // is_movable // ////////////////////////////////////////////////////////////////////////////// @@ -230,6 +301,7 @@ // ////////////////////////////////////////////////////////////////////////////// +/* originals template <class T> typename enable_if< ::BOOST_MOVE_NAMESPACE::move_detail::is_rv<T>, T &>::type forward(const typename move_detail::identity<T>::type &x) @@ -243,7 +315,86 @@ { return x; } +*/ +// explicitly lvalue reference +template < typename T > +typename enable_if< + typename mpl::and_< + typename mpl::not_< ::BOOST_MOVE_NAMESPACE::move_detail::is_rv_ref< T > >::type, + is_lvalue_reference< T > >::type, + const T & >::type +forward( typename ::BOOST_MOVE_NAMESPACE::move_detail::identity< T >::type x ) +{ + BOOST_STATIC_ASSERT( !is_lvalue_reference< T >::value ); + return x; +} + +// rvalue reference +template < typename T > +typename enable_if< + ::BOOST_MOVE_NAMESPACE::move_detail::is_rv< typename remove_reference< T >::type >, + typename remove_reference< T >::type & >::type +forward( typename ::BOOST_MOVE_NAMESPACE::move_detail::identity< T >::type &x ) +{ + return const_cast< T & >( x ); +} + +// lvalue reference +template < typename T > +typename disable_if< + typename mpl::or_< + ::BOOST_MOVE_NAMESPACE::move_detail::is_rv_ref< T >, + is_lvalue_reference< T > >::type, + const typename remove_reference< T >::type & >::type +forward( typename ::BOOST_MOVE_NAMESPACE::move_detail::identity< T >::type &x ) +{ + return x; +} + +namespace move_detail +{ + +template < typename T, bool > +struct forward_helper +{ + template < typename U > + forward_helper( U & ); +}; + +template < typename T > +struct forward_helper< T, false > +{ + forward_helper( const T &x ) + : r( static_cast< const BOOST_RV_REF( T ) >( x ) ) {} + + const BOOST_RV_REF( T ) r; +}; + +} // namespace move_detail + +// implicitly const Lv-ref reference +template < typename T > +typename disable_if< + typename mpl::or_< + ::BOOST_MOVE_NAMESPACE::move_detail::is_rv_ref< T >, + is_lvalue_reference< T > >::type, + const typename remove_reference< T >::type & >::type +forward( ::BOOST_MOVE_NAMESPACE::move_detail::forward_helper< T, true > x ); + +// reference to temporary obj +template < typename T > +typename lazy_disable_if< + typename mpl::or_< + ::BOOST_MOVE_NAMESPACE::move_detail::is_rv_ref< T >, + is_lvalue_reference< T > >::type, + ::BOOST_MOVE_NAMESPACE::move_detail::add_rv_ref< T > >::type +forward( ::BOOST_MOVE_NAMESPACE::move_detail::forward_helper< T, false > x ) +{ + using ::BOOST_MOVE_NAMESPACE::move_detail::add_rv_ref; + return const_cast< typename add_rv_ref< T >::type >( x.r ); +} + #else //BOOST_MOVE_OPTIMIZED_EMULATION #define BOOST_COPY_ASSIGN_REF(TYPE)\
糞長いdiffですません...
とりあえずこれを適用すると、以下のコードが期待したとおりに動くはずです。
#include <boost/move/move.hpp> struct S { BOOST_COPYABLE_AND_MOVABLE( S ) public: S() {} S( BOOST_COPY_ASSIGN_REF( S ) ) {} S( BOOST_RV_REF( S ) ) {} void operator=( const BOOST_RV_REF( S ) ) {} void f( void ) const {} }; int main( void ) { { S s = boost::forward< BOOST_RV_REF( S ) >( S() ); BOOST_RV_REF( S ) rs = boost::forward< BOOST_RV_REF( S ) >( s ); rs.f(); } { S s = boost::forward< S >( S() ); const S &ls = boost::forward< S >( s ); ls.f(); } { const S s; const S &ls = boost::forward< const S >( s ); ls.f(); } { const S s; boost::forward< S >( s ).f(); // compile error } }
最後のforwardは曖昧な関数呼出になってエラーになるはずです。できればstatic_assertとかにしたかったけど、まぁ及第点ぐらい?
msvcでの実験結果とかborlandでの結果とか送ってもらえたりしたら嬉しいです。手元でテストできないだろうから修正できるか分からないけど・・・