Index: detail/backup_holder.hpp =================================================================== --- detail/backup_holder.hpp (revision 81014) +++ detail/backup_holder.hpp (working copy) @@ -13,6 +13,7 @@ #ifndef BOOST_VARIANT_DETAIL_BACKUP_HOLDER_HPP #define BOOST_VARIANT_DETAIL_BACKUP_HOLDER_HPP +#include "boost/config.hpp" #include "boost/assert.hpp" namespace boost { @@ -32,7 +33,7 @@ delete backup_; } - explicit backup_holder(T* backup) + explicit backup_holder(T* backup) BOOST_NOEXCEPT : backup_(backup) { } @@ -53,7 +54,7 @@ return *this; } - void swap(backup_holder& rhs) + void swap(backup_holder& rhs) BOOST_NOEXCEPT { T* tmp = rhs.backup_; rhs.backup_ = this->backup_; @@ -83,7 +84,7 @@ } template -void swap(backup_holder& lhs, backup_holder& rhs) +void swap(backup_holder& lhs, backup_holder& rhs) BOOST_NOEXCEPT { lhs.swap(rhs); } Index: detail/move.hpp =================================================================== --- detail/move.hpp (revision 81014) +++ detail/move.hpp (working copy) @@ -82,6 +82,8 @@ } // namespace detail +#ifdef BOOST_NO_RVALUE_REFERENCES + template inline typename detail::move_type::type @@ -93,6 +95,12 @@ return move_t(source); } +#else + +using std::move; + +#endif + ////////////////////////////////////////////////////////////////////////// // class template return_t // Index: variant.hpp =================================================================== --- variant.hpp (revision 81014) +++ variant.hpp (working copy) @@ -51,7 +51,9 @@ #include "boost/type_traits/has_nothrow_copy.hpp" #include "boost/type_traits/is_const.hpp" #include "boost/type_traits/is_same.hpp" +#include "boost/type_traits/is_rvalue_reference.hpp" #include "boost/utility/enable_if.hpp" +#include "boost/utility/declval.hpp" #include "boost/variant/recursive_wrapper_fwd.hpp" #include "boost/variant/static_visitor.hpp" @@ -338,7 +340,7 @@ public: // visitor interface - T& operator()(T& operand) const + T& operator()(T& operand) const BOOST_NOEXCEPT { return operand; } @@ -397,7 +399,7 @@ public: // structors - explicit copy_into(void* storage) + explicit copy_into(void* storage) BOOST_NOEXCEPT : storage_(storage) { } @@ -431,6 +433,46 @@ }; /////////////////////////////////////////////////////////////////////////////// +// (detail) class move_into +// +// Internal visitor that moves the value it visits into the given buffer. +// +#ifndef BOOST_NO_RVALUE_REFERENCES +class move_into + : public static_visitor<> +{ +private: // representation + + void* storage_; + +public: // structors + + explicit move_into(void* storage) BOOST_NOEXCEPT + : storage_(storage) + { + } + +public: // internal visitor interface + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(boost::detail::variant::backup_holder& operand, long) const + { + new(storage_) T( ::boost::detail::variant::move(operand.get()) ); + BOOST_VARIANT_AUX_RETURN_VOID; + } + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(T& operand, int) const BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(boost::declval()))) + { + new(storage_) T(::boost::detail::variant::move(operand)); + BOOST_VARIANT_AUX_RETURN_VOID; + } +}; +#endif + +/////////////////////////////////////////////////////////////////////////////// // (detail) class assign_storage // // Internal visitor that assigns the given storage (which must be a @@ -445,7 +487,7 @@ public: // structors - explicit assign_storage(const void* rhs_storage) + explicit assign_storage(const void* rhs_storage) BOOST_NOEXCEPT : rhs_storage_(rhs_storage) { } @@ -488,6 +530,63 @@ }; /////////////////////////////////////////////////////////////////////////////// +// (detail) class move_storage +// +// Internal visitor that moves the given storage (which must be a +// constructed value of the same type) to the value it visits. +// +struct move_storage + : public static_visitor<> +{ +private: // representation + + void* rhs_storage_; + +public: // structors + + explicit move_storage(void* rhs_storage) BOOST_NOEXCEPT + : rhs_storage_(rhs_storage) + { + } + +public: // internal visitor interfaces + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(backup_holder& lhs_content, long) const + { + lhs_content.get() + = ::boost::detail::variant::move(static_cast* >(rhs_storage_)->get()); + BOOST_VARIANT_AUX_RETURN_VOID; + } + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(const backup_holder& lhs_content, long) const + { + lhs_content.get() + = ::boost::detail::variant::move(static_cast* >(rhs_storage_)->get()); + BOOST_VARIANT_AUX_RETURN_VOID; + } + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(T& lhs_content, int) const + { + // NOTE TO USER : + // Compile error here indicates one of variant's bounded types does + // not meet the requirements of the Assignable concept. Thus, + // variant is not Assignable. + // + // Hint: Are any of the bounded types const-qualified or references? + // + lhs_content = ::boost::detail::variant::move(*static_cast(rhs_storage_)); + BOOST_VARIANT_AUX_RETURN_VOID; + } + +}; + +/////////////////////////////////////////////////////////////////////////////// // (detail) class direct_assigner // // Generic static visitor that: if and only if the visited value is of the @@ -504,7 +603,7 @@ public: // structors - explicit direct_assigner(const T& rhs) + explicit direct_assigner(const T& rhs) BOOST_NOEXCEPT : rhs_(rhs) { } @@ -520,7 +619,7 @@ } template - bool operator()(U&) + bool operator()(U&) BOOST_NOEXCEPT { return false; } @@ -560,6 +659,65 @@ }; /////////////////////////////////////////////////////////////////////////////// +// (detail) class direct_mover +// +// Generic static visitor that: if and only if the visited value is of the +// specified type, move assigns the given value to the visited value and returns +// true; else returns false. +// +template +class direct_mover + : public static_visitor +{ +private: // representation + + T& rhs_; + +public: // structors + + explicit direct_mover(T& rhs) BOOST_NOEXCEPT + : rhs_(rhs) + { + } + +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + +public: // visitor interface + + bool operator()(T& lhs) + { + lhs = ::boost::detail::variant::move(rhs_); + return true; + } + + template + bool operator()(U&) BOOST_NOEXCEPT + { + return false; + } + +#else // MSVC6 + +public: // visitor interface + + template + bool operator()(U& lhs) + { + // MSVC6 can not use direct_mover class + return direct_assigner(rhs_)(lhs); + } + +#endif // MSVC6 workaround + +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) +private: + // silence MSVC warning C4512: assignment operator could not be generated + direct_mover& operator= (direct_mover const&); +#endif +}; + + +/////////////////////////////////////////////////////////////////////////////// // (detail) class backup_assigner // // Internal visitor that "assigns" the given value to the visited value, @@ -745,7 +903,7 @@ public: // visitor interfaces template - const std::type_info& operator()(const T&) const + const std::type_info& operator()(const T&) const BOOST_NOEXCEPT { return typeid(T); } @@ -772,7 +930,7 @@ public: // structors - explicit comparer(const Variant& lhs) + explicit comparer(const Variant& lhs) BOOST_NOEXCEPT : lhs_(lhs) { } @@ -844,7 +1002,7 @@ public: // structors - explicit invoke_visitor(Visitor& visitor) + explicit invoke_visitor(Visitor& visitor) BOOST_NOEXCEPT : visitor_(visitor) { } @@ -1170,26 +1328,26 @@ which_t which_; storage_t storage_; - void indicate_which(int which_arg) + void indicate_which(int which_arg) BOOST_NOEXCEPT { which_ = static_cast( which_arg ); } - void indicate_backup_which(int which_arg) + void indicate_backup_which(int which_arg) BOOST_NOEXCEPT { which_ = static_cast( -(which_arg + 1) ); } private: // helpers, for queries (below) - bool using_backup() const + bool using_backup() const BOOST_NOEXCEPT { return which_ < 0; } public: // queries - int which() const + int which() const BOOST_NOEXCEPT { // If using heap backup... if (using_backup()) @@ -1222,7 +1380,7 @@ destroy_content(); } - variant() + variant() BOOST_NOEXCEPT_IF(boost::has_nothrow_constructor::type::value) { // NOTE TO USER : // Compile error from here indicates that the first bound @@ -1244,7 +1402,7 @@ public: // structors - explicit convert_copy_into(void* storage) + explicit convert_copy_into(void* storage) BOOST_NOEXCEPT : storage_(storage) { } @@ -1454,7 +1612,19 @@ // ...and activate the *this's primary storage on success: indicate_which(operand.which()); } + +#ifndef BOOST_NO_RVALUE_REFERENCES + variant(variant&& operand) + { + // Move the value of operand into *this... + detail::variant::move_into visitor( storage_.address() ); + operand.internal_apply_visitor(visitor); + // ...and activate the *this's primary storage on success: + indicate_which(operand.which()); + } +#endif + private: // helpers, for modifiers (below) # if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) @@ -1478,7 +1648,7 @@ public: // structors - assigner(variant& lhs, int rhs_which) + assigner(variant& lhs, int rhs_which) BOOST_NOEXCEPT : lhs_(lhs) , rhs_which_(rhs_which) { @@ -1605,9 +1775,154 @@ assigner& operator= (assigner const&); #endif }; - + friend class assigner; + +#ifndef BOOST_NO_RVALUE_REFERENCES + // class move_assigner + // + // Internal visitor that "move assigns" the visited value to the given variant + // by appropriate destruction and move-construction. + // + class move_assigner + : public static_visitor<> + { + private: // representation + + variant& lhs_; + int rhs_which_; + + public: // structors + + move_assigner(variant& lhs, int rhs_which) BOOST_NOEXCEPT + : lhs_(lhs) + , rhs_which_(rhs_which) + { + } + + private: // helpers, for internal visitor interface (below) + + template + void assign_impl( + RhsT& rhs_content + , mpl::true_// has_nothrow_copy + , mpl::false_// has_nothrow_move_constructor + , B2// has_fallback_type + ) + { + // Destroy lhs's content... + lhs_.destroy_content(); // nothrow + + // ...copy rhs content into lhs's storage... + new(lhs_.storage_.address()) + RhsT( rhs_content ); // nothrow + + // ...and indicate new content type: + lhs_.indicate_which(rhs_which_); // nothrow + } + + template + void assign_impl( + RhsT& rhs_content + , mpl::true_// has_nothrow_copy + , mpl::true_// has_nothrow_move_constructor + , B// has_fallback_type + ) + { + // ...destroy lhs's content... + lhs_.destroy_content(); // nothrow + + // ...move the rhs_content into lhs's storage... + new(lhs_.storage_.address()) + RhsT( detail::variant::move(rhs_content) ); // nothrow + + // ...and indicate new content type: + lhs_.indicate_which(rhs_which_); // nothrow + } + + template + void assign_impl( + RhsT& rhs_content + , mpl::false_// has_nothrow_copy + , mpl::false_// has_nothrow_move_constructor + , mpl::true_// has_fallback_type + ) + { + // Destroy lhs's content... + lhs_.destroy_content(); // nothrow + + try + { + // ...and attempt to copy rhs's content into lhs's storage: + new(lhs_.storage_.address()) + RhsT( detail::variant::move(rhs_content) ); + } + catch (...) + { + // In case of failure, default-construct fallback type in lhs's storage... + new (lhs_.storage_.address()) + fallback_type_; // nothrow + + // ...indicate construction of fallback type... + lhs_.indicate_which( + BOOST_MPL_AUX_VALUE_WKND(fallback_type_index_)::value + ); // nothrow + + // ...and rethrow: + throw; + } + + // In the event of success, indicate new content type: + lhs_.indicate_which(rhs_which_); // nothrow + } + + template + void assign_impl( + const RhsT& rhs_content + , mpl::false_// has_nothrow_copy + , mpl::false_// has_nothrow_move_constructor + , mpl::false_// has_fallback_type + ) + { + detail::variant::backup_assigner + visitor(lhs_, rhs_which_, rhs_content); + lhs_.internal_apply_visitor(visitor); + } + + public: // internal visitor interfaces + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(RhsT& rhs_content, int) + { + typedef typename detail::variant::has_nothrow_move_constructor::type + nothrow_move_constructor; + typedef typename mpl::or_< // reduces compile-time + nothrow_move_constructor + , has_nothrow_copy + >::type nothrow_copy; + + assign_impl( + rhs_content + , nothrow_copy() + , nothrow_move_constructor() + , has_fallback_type_() + ); + + BOOST_VARIANT_AUX_RETURN_VOID; + } + +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) + private: + // silence MSVC warning C4512: assignment operator could not be generated + move_assigner& operator= (move_assigner const&); +#endif + }; + + friend class move_assigner; +#endif // BOOST_NO_RVALUE_REFERENCES + void variant_assign(const variant& rhs) { // If the contained types are EXACTLY the same... @@ -1625,6 +1940,25 @@ } } +#ifndef BOOST_NO_RVALUE_REFERENCES + void variant_assign(variant&& rhs) + { + // If the contained types are EXACTLY the same... + if (which_ == rhs.which_) + { + // ...then move rhs's storage to lhs's content: + detail::variant::move_storage visitor(rhs.storage_.address()); + this->internal_apply_visitor(visitor); + } + else + { + // Otherwise, perform general (move-based) variant assignment: + move_assigner visitor(*this, rhs.which()); + rhs.internal_apply_visitor(visitor); + } + } +#endif // BOOST_NO_RVALUE_REFERENCES + private: // helpers, for modifiers (below) template @@ -1645,8 +1979,37 @@ } } +#ifndef BOOST_NO_RVALUE_REFERENCES + template + void move_assign(T&& rhs) + { + // If direct T-to-T move assignment is not possible... + detail::variant::direct_mover direct_move(rhs); + if (this->apply_visitor(direct_move) == false) + { + // ...then convert rhs to variant and assign: + // + // While potentially inefficient, the following construction of a + // variant allows T as any type convertible to one of the bounded + // types without excessive code redundancy. + // + variant temp( detail::variant::move(rhs) ); + variant_assign( detail::variant::move(temp) ); + } + } +#endif // BOOST_NO_RVALUE_REFERENCES + public: // modifiers +#ifndef BOOST_NO_RVALUE_REFERENCES + template + typename boost::enable_if, variant& >::type operator=(T&& rhs) + { + move_assign( detail::variant::move(rhs) ); + return *this; + } +#endif // BOOST_NO_RVALUE_REFERENCES + template variant& operator=(const T& rhs) { @@ -1661,6 +2024,14 @@ return *this; } +#ifndef BOOST_NO_RVALUE_REFERENCES + variant& operator=(variant&& rhs) + { + variant_assign( detail::variant::move(rhs) ); + return *this; + } +#endif // BOOST_NO_RVALUE_REFERENCES + void swap(variant& rhs) { // If the contained types are the same... @@ -1685,7 +2056,7 @@ // NOTE: member which() defined above. // - bool empty() const + bool empty() const BOOST_NOEXCEPT { return false; }