diff --git a/include/boost/variant2/variant.hpp b/include/boost/variant2/variant.hpp index aa9441a..4c5f5a9 100644 --- a/include/boost/variant2/variant.hpp +++ b/include/boost/variant2/variant.hpp @@ -58,19 +58,6 @@ constexpr bool operator>=(monostate, monostate) noexcept { return true; } constexpr bool operator==(monostate, monostate) noexcept { return true; } constexpr bool operator!=(monostate, monostate) noexcept { return false; } -// valueless - -struct valueless -{ -}; - -constexpr bool operator<(valueless, valueless) noexcept { return false; } -constexpr bool operator>(valueless, valueless) noexcept { return false; } -constexpr bool operator<=(valueless, valueless) noexcept { return true; } -constexpr bool operator>=(valueless, valueless) noexcept { return true; } -constexpr bool operator==(valueless, valueless) noexcept { return true; } -constexpr bool operator!=(valueless, valueless) noexcept { return false; } - // variant forward declaration template class variant; @@ -459,7 +446,8 @@ template using resolve_overload_index = mp_find using can_be_valueless = std::is_same>, valueless>; +template using can_be_valueless = mp_any..., std::is_nothrow_default_constructible...>; +template using valueless_index = mp_if, monostate>, mp_find, monostate>, mp_find_if, std::is_nothrow_default_constructible>>; template struct variant_base_impl; // trivially destructible, single buffered template using variant_base = variant_base_impl...>::value, mp_any...>, can_be_valueless>::value, T...>; @@ -519,9 +507,11 @@ template struct variant_base_impl template void emplace_impl( mp_false, mp_false, A&&... a ) { - if( can_be_valueless::value ) // T0 == valueless + if( can_be_valueless::value ) { - std::size_t const K = 0; + std::size_t const K = valueless_index::value; + + assert( K < sizeof...(T) ); try { @@ -681,9 +671,11 @@ template struct variant_base_impl st1_.emplace( mp_size_t(), std::forward(a)... ); ix_ = J; } - else if( can_be_valueless::value ) // T0 == valueless + else if( can_be_valueless::value ) { - std::size_t const K = 0; + std::size_t const K = valueless_index::value; + + assert( K < sizeof...(T) ); _destroy(); diff --git a/test/variant_valueless.cpp b/test/variant_valueless.cpp index 77bb166..15dad2e 100644 --- a/test/variant_valueless.cpp +++ b/test/variant_valueless.cpp @@ -17,217 +17,237 @@ using namespace boost::variant2; #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) -enum E1 -{ - e1 -}; +// -enum E2 -{ - e2 -}; +enum E1 { e1 }; +enum E1x { e1x }; struct X1 { X1() = default; - X1( E2 ) { throw std::runtime_error( "X1(E2)" ); } + + X1( E1 ) noexcept {} + X1( E1x ) { throw std::runtime_error( "X1(E1x)" ); } }; +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_constructible::value ); STATIC_ASSERT( std::is_trivially_destructible::value ); +STATIC_ASSERT( std::is_trivially_move_assignable::value ); +STATIC_ASSERT( std::is_nothrow_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_constructible::value ); + +enum E2 { e2 }; +enum E2x { e2x }; struct X2 { - X2() = default; + X2(); ~X2(); - X2( E2 ) { throw std::runtime_error( "X1(E2)" ); } + + X2( E2 ) noexcept {} + X2( E2x ) { throw std::runtime_error( "X2(E2x)" ); } }; +X2::X2() {} X2::~X2() {} +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_constructible::value ); STATIC_ASSERT( !std::is_trivially_destructible::value ); +STATIC_ASSERT( std::is_nothrow_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_constructible::value ); + +enum E3 { e3 }; +enum E3x { e3x }; struct X3 { - X3() = default; + X3(); + X3( X3 const& ) {} X3( X3&& ) {} + + X3( E3 ) noexcept {} + X3( E3x ) { throw std::runtime_error( "X3(E3x)" ); } + + X3& operator=( X3 const& ) = default; + X3& operator=( X3&& ) = default; }; +X3::X3() {} + +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_trivially_destructible::value ); +STATIC_ASSERT( std::is_trivially_move_assignable::value ); +STATIC_ASSERT( std::is_nothrow_constructible::value ); +STATIC_ASSERT( !std::is_nothrow_constructible::value ); + +// + +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_trivially_destructible::value ); + +// int main() { { - variant v; + variant v; - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); + BOOST_TEST_EQ( v.index(), 0 ); try { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); + v = e1x; + BOOST_ERROR( "`v = e1x;` failed to throw" ); } catch( std::exception const& ) { + // basic guarantee; X1 is nothrow default-constructible BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); } } { - variant v; - - v = e1; + variant v( e2 ); BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); try { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); - } - catch( std::exception const& ) - { - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - } - } - - { - variant v; - - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - - try - { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); - } - catch( std::exception const& ) - { - // all types are trivial, so we get a constexpr path - // and v.index() stays 1 - - BOOST_TEST( v.index() == 0 || v.index() == 1 ); - - if( v.index() == 1 ) - { - BOOST_TEST_EQ( get<1>(v), e1 ); - } - } - } - - { - variant v; - - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - - try - { - v = e2; - BOOST_ERROR( "`v = e2` failed to throw" ); - } - catch( std::exception const& ) - { - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - } - } - - { - variant v; - - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - - try - { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); - } - catch( std::exception const& ) - { - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - } - } - - { - variant v; - - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - - try - { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); - } - catch( std::exception const& ) - { - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - } - } - - { - variant v; - - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); - - try - { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); + v = e1x; + BOOST_ERROR( "`v = e1x;` failed to throw" ); } catch( std::exception const& ) { + // basic guarantee; X1 is nothrow default-constructible BOOST_TEST_EQ( v.index(), 0 ); } } { - variant v; + variant v; - v = e1; - - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); + BOOST_TEST_EQ( v.index(), 0 ); try { - v = e2; - BOOST_ERROR( "`v = e2;` failed to throw" ); + v = e1x; + BOOST_ERROR( "`v = e1x;` failed to throw" ); } catch( std::exception const& ) { - BOOST_TEST_EQ( v.index(), 1 ); - BOOST_TEST_EQ( get<1>(v), e1 ); + // basic guarantee; monostate + BOOST_TEST_EQ( v.index(), 2 ); + } + } + + { + variant v( e2 ); + + BOOST_TEST_EQ( v.index(), 1 ); + + try + { + v = e1x; + BOOST_ERROR( "`v = e1x;` failed to throw" ); + } + catch( std::exception const& ) + { + // basic guarantee; monostate + BOOST_TEST_EQ( v.index(), 2 ); + } + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + + try + { + v = e3x; + BOOST_ERROR( "`v = e3x;` failed to throw" ); + } + catch( std::exception const& ) + { + // basic guarantee; X1 is nothrow default-constructible + BOOST_TEST_EQ( v.index(), 2 ); + } + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + + try + { + v = e3x; + BOOST_ERROR( "`v = e3x;` failed to throw" ); + } + catch( std::exception const& ) + { + // basic guarantee; monostate + BOOST_TEST_EQ( v.index(), 3 ); + } + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + + try + { + v = e3x; + BOOST_ERROR( "`v = e3x;` failed to throw" ); + } + catch( std::exception const& ) + { + // double buffered, no change + BOOST_TEST_EQ( v.index(), 0 ); + } + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + + try + { + v = e1x; + BOOST_ERROR( "`v = e1x;` failed to throw" ); + } + catch( std::exception const& ) + { + // all trivially destructible and move-assignable, no change + BOOST_TEST_EQ( v.index(), 0 ); + } + } + + { + variant v; + + BOOST_TEST_EQ( v.index(), 0 ); + + try + { + v = e1x; + BOOST_ERROR( "`v = e1x;` failed to throw" ); + } + catch( std::exception const& ) + { + // all trivially destructible and move-assignable, no change + BOOST_TEST_EQ( v.index(), 0 ); } }