diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index 8f6a223..0155de9 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -16,6 +16,7 @@ #include #include #include +#include // @@ -353,7 +354,7 @@ template struct variant_base_impl { } - template constexpr mp_at_c, I>& _get_impl( mp_size_t i ) noexcept + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -362,7 +363,7 @@ template struct variant_base_impl return st1_.get( mp_size_t() ); } - template constexpr mp_at_c, I> const& _get_impl( mp_size_t i ) const noexcept + template constexpr mp_at_c, I> const& _get_impl( mp_size_t ) const noexcept { size_t const J = I+1; @@ -407,7 +408,7 @@ template struct variant_base_impl { } - template constexpr mp_at_c, I>& _get_impl( mp_size_t i ) noexcept + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -417,7 +418,7 @@ template struct variant_base_impl return ix_ >= 0? st1_.get( j ): st2_.get( j ); } - template constexpr mp_at_c, I> const& _get_impl( mp_size_t i ) const noexcept + template constexpr mp_at_c, I> const& _get_impl( mp_size_t ) const noexcept { size_t const J = I+1; @@ -478,7 +479,7 @@ template struct variant_base_impl _destroy(); } - template constexpr mp_at_c, I>& _get_impl( mp_size_t i ) noexcept + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -487,7 +488,7 @@ template struct variant_base_impl return st1_.get( mp_size_t() ); } - template constexpr mp_at_c, I> const& _get_impl( mp_size_t i ) const noexcept + template constexpr mp_at_c, I> const& _get_impl( mp_size_t ) const noexcept { size_t const J = I+1; @@ -563,7 +564,7 @@ template struct variant_base_impl _destroy(); } - template constexpr mp_at_c, I>& _get_impl( mp_size_t i ) noexcept + template constexpr mp_at_c, I>& _get_impl( mp_size_t ) noexcept { size_t const J = I+1; @@ -573,7 +574,7 @@ template struct variant_base_impl return ix_ >= 0? st1_.get( j ): st2_.get( j ); } - template constexpr mp_at_c, I> const& _get_impl( mp_size_t i ) const noexcept + template constexpr mp_at_c, I> const& _get_impl( mp_size_t ) const noexcept { size_t const J = I+1; @@ -638,6 +639,55 @@ template struct is_in_place_index>: std::true } // namespace detail +// is_nothrow_swappable + +namespace detail +{ + +namespace det2 +{ + +using std::swap; + +template using is_swappable_impl = decltype(swap(std::declval(), std::declval())); + +#if BOOST_WORKAROUND( BOOST_MSVC, <= 1910 ) + +template struct is_nothrow_swappable_impl_ +{ + static constexpr bool value = noexcept(swap(std::declval(), std::declval())); +}; + +template using is_nothrow_swappable_impl = mp_bool< is_nothrow_swappable_impl_::value >; + +#else + +template using is_nothrow_swappable_impl = std::enable_if_t(), std::declval()))>; + +#endif + +} // namespace det2 + +template struct is_swappable: mp_valid +{ +}; + +#if BOOST_WORKAROUND( BOOST_MSVC, <= 1910 ) + +template struct is_nothrow_swappable: mp_eval_if>, mp_false, det2::is_nothrow_swappable_impl, T> +{ +}; + +#else + +template struct is_nothrow_swappable: mp_valid +{ +}; + +#endif + +} // namespace detail + // variant template class variant: private variant2::detail::variant_base @@ -715,7 +765,7 @@ public: { mp_for_each>([&]( auto I ){ - constexpr auto J = decltype(I)::value; + constexpr auto J = I.value; if( J == r.index() ) { @@ -738,7 +788,7 @@ public: { mp_for_each>([&]( auto I ){ - constexpr auto J = decltype(I)::type::value; + constexpr auto J = I.value; if( J == r.index() ) { @@ -805,7 +855,29 @@ public: // swap - void swap( variant& r ); // noexcept( ... ) + void swap( variant& r ) noexcept( mp_all..., variant2::detail::is_nothrow_swappable...>::value ) + { + if( index() == r.index() ) + { + mp_for_each>([&]( auto I ){ + + constexpr auto J = I.value; + + if( J == this->index() ) + { + using std::swap; + swap( get(*this), get(r) ); + } + + }); + } + else + { + variant tmp( std::move(*this) ); + *this = std::move( r ); + r = std::move( tmp ); + } + } // private accessors @@ -916,7 +988,10 @@ template constexpr bool operator>=( variant const & v, variant template constexpr void visit( Visitor&&, Variants&&... ); // specialized algorithms -template void swap( variant & v, variant & w ); // noexcept(see below ); +template void swap( variant & v, variant & w ) noexcept( noexcept(v.swap(w)) ) +{ + v.swap( w ); +} } // namespace variant2 } // namespace boost diff --git a/test/Jamfile b/test/Jamfile index 07e9465..1670e6a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -34,3 +34,4 @@ run variant_move_assign.cpp : : : $(REQ) ; run variant_value_assign.cpp : : : $(REQ) ; run variant_emplace_index.cpp : : : $(REQ) ; run variant_emplace_type.cpp : : : $(REQ) ; +run variant_swap.cpp : : : $(REQ) ; diff --git a/test/variant_swap.cpp b/test/variant_swap.cpp new file mode 100644 index 0000000..679f5f8 --- /dev/null +++ b/test/variant_swap.cpp @@ -0,0 +1,259 @@ + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +using namespace boost::variant2; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +struct X1 +{ + int v; + + X1(): v(0) {} + explicit X1(int v): v(v) {} + X1(X1 const& r): v(r.v) {} + X1(X1&& r): v(r.v) {} + X1& operator=( X1 const& r ) { v = r.v; return *this; } + X1& operator=( X1&& r ) { v = r.v; return *this; } +}; + +inline bool operator==( X1 const& a, X1 const& b ) { return a.v == b.v; } + +STATIC_ASSERT( !std::is_nothrow_default_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_copy_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_move_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_copy_assignable::value ); +STATIC_ASSERT( !std::is_nothrow_move_assignable::value ); + +struct X2 +{ + int v; + + X2(): v(0) {} + explicit X2(int v): v(v) {} + X2(X2 const& r): v(r.v) {} + X2(X2&& r): v(r.v) {} + X2& operator=( X2 const& r ) { v = r.v; return *this; } + X2& operator=( X2&& r ) { v = r.v; return *this; } +}; + +inline bool operator==( X2 const& a, X2 const& b ) { return a.v == b.v; } + +STATIC_ASSERT( !std::is_nothrow_default_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_copy_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_move_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_copy_assignable::value ); +STATIC_ASSERT( !std::is_nothrow_move_assignable::value ); + +int main() +{ + { + variant v; + BOOST_TEST_EQ( get<0>(v), 0 ); + + variant v2( 1 ); + BOOST_TEST_EQ( get<0>(v2), 1 ); + + swap( v, v2 ); + BOOST_TEST_EQ( get<0>(v), 1 ); + BOOST_TEST_EQ( get<0>(v2), 0 ); + + variant v3( 2 ); + BOOST_TEST_EQ( get<0>(v3), 2 ); + + swap( v, v3 ); + BOOST_TEST_EQ( get<0>(v), 2 ); + BOOST_TEST_EQ( get<0>(v3), 1 ); + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + BOOST_TEST_EQ( get<0>(v), 0 ); + + variant v2( 1 ); + + BOOST_TEST_EQ( v2.index(), 0 ); + BOOST_TEST_EQ( get<0>(v2), 1 ); + + swap( v, v2 ); + + BOOST_TEST_EQ( v.index(), 0 ); + BOOST_TEST_EQ( get<0>(v), 1 ); + + BOOST_TEST_EQ( v2.index(), 0 ); + BOOST_TEST_EQ( get<0>(v2), 0 ); + + variant v3( 3.14f ); + + BOOST_TEST_EQ( v3.index(), 1 ); + BOOST_TEST_EQ( get<1>(v3), 3.14f ); + + swap( v, v3 ); + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), 3.14f ); + + BOOST_TEST_EQ( v3.index(), 0 ); + BOOST_TEST_EQ( get<0>(v3), 1 ); + + variant v4( 3.15f ); + + BOOST_TEST_EQ( v4.index(), 1 ); + BOOST_TEST_EQ( get<1>(v4), 3.15f ); + + swap( v, v4 ); + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), 3.15f ); + + BOOST_TEST_EQ( v4.index(), 1 ); + BOOST_TEST_EQ( get<1>(v4), 3.14f ); + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + BOOST_TEST_EQ( get<0>(v), 0 ); + + variant v2( in_place_index<1>, 1 ); + + BOOST_TEST_EQ( v2.index(), 1 ); + BOOST_TEST_EQ( get<1>(v2), 1 ); + + swap( v, v2 ); + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v), 1 ); + + BOOST_TEST_EQ( v2.index(), 0 ); + BOOST_TEST_EQ( get<0>(v2), 0 ); + + variant v3( 3.14f ); + + BOOST_TEST_EQ( v3.index(), 2 ); + BOOST_TEST_EQ( get<2>(v3), 3.14f ); + + swap( v, v3 ); + + BOOST_TEST_EQ( v.index(), 2 ); + BOOST_TEST_EQ( get<2>(v), 3.14f ); + + BOOST_TEST_EQ( v3.index(), 1 ); + BOOST_TEST_EQ( get<1>(v3), 1 ); + + variant v4( 3.15f ); + + BOOST_TEST_EQ( v4.index(), 2 ); + BOOST_TEST_EQ( get<2>(v4), 3.15f ); + + swap( v, v4 ); + + BOOST_TEST_EQ( v.index(), 2 ); + BOOST_TEST_EQ( get<2>(v), 3.15f ); + + BOOST_TEST_EQ( v4.index(), 2 ); + BOOST_TEST_EQ( get<2>(v4), 3.14f ); + + variant v5( "s1" ); + + BOOST_TEST_EQ( v5.index(), 3 ); + BOOST_TEST_EQ( get<3>(v5), std::string("s1") ); + + swap( v, v5 ); + + BOOST_TEST_EQ( v.index(), 3 ); + BOOST_TEST_EQ( get<3>(v), std::string("s1") ); + + BOOST_TEST_EQ( v5.index(), 2 ); + BOOST_TEST_EQ( get<2>(v5), 3.15f ); + + variant v6( "s2" ); + + BOOST_TEST_EQ( v6.index(), 3 ); + BOOST_TEST_EQ( get<3>(v6), std::string("s2") ); + + swap( v, v6 ); + + BOOST_TEST_EQ( v.index(), 3 ); + BOOST_TEST_EQ( get<3>(v), std::string("s2") ); + + BOOST_TEST_EQ( v6.index(), 3 ); + BOOST_TEST_EQ( get<3>(v6), std::string("s1") ); + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + BOOST_TEST_EQ( get<0>(v).v, 0 ); + + variant v2( X1{1} ); + + BOOST_TEST_EQ( v2.index(), 0 ); + BOOST_TEST_EQ( get<0>(v2).v, 1 ); + + swap( v, v2 ); + + BOOST_TEST_EQ( v.index(), 0 ); + BOOST_TEST_EQ( get<0>(v).v, 1 ); + + BOOST_TEST_EQ( v2.index(), 0 ); + BOOST_TEST_EQ( get<0>(v2).v, 0 ); + + variant v3( in_place_index<1>, 2 ); + + BOOST_TEST_EQ( v3.index(), 1 ); + BOOST_TEST_EQ( get<1>(v3).v, 2 ); + + swap( v, v3 ); + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v).v, 2 ); + + BOOST_TEST_EQ( v3.index(), 0 ); + BOOST_TEST_EQ( get<0>(v3).v, 1 ); + + variant v4( in_place_index<1>, 3 ); + + BOOST_TEST_EQ( v4.index(), 1 ); + BOOST_TEST_EQ( get<1>(v4).v, 3 ); + + swap( v, v4 ); + + BOOST_TEST_EQ( v.index(), 1 ); + BOOST_TEST_EQ( get<1>(v).v, 3 ); + + BOOST_TEST_EQ( v4.index(), 1 ); + BOOST_TEST_EQ( get<1>(v4).v, 2 ); + + variant v5( in_place_index<0>, 4 ); + + BOOST_TEST_EQ( v5.index(), 0 ); + BOOST_TEST_EQ( get<0>(v5).v, 4 ); + + swap( v, v5 ); + + BOOST_TEST_EQ( v.index(), 0 ); + BOOST_TEST_EQ( get<0>(v).v, 4 ); + + BOOST_TEST_EQ( v5.index(), 1 ); + BOOST_TEST_EQ( get<1>(v5).v, 3 ); + } + + return boost::report_errors(); +}