From 458ef69052224b5d3d2c78cfbe0a0e0ec85a4193 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Tue, 6 Dec 2016 11:26:48 +0000 Subject: [PATCH] enable_special_members.h: Make _Enable_default_constructor constexpr. * include/bits/enable_special_members.h: Make _Enable_default_constructor constexpr. * include/std/variant (variant::emplace, variant::swap, std::swap, std::hash): Sfinae on emplace and std::swap; handle __poison_hash bases of duplicated types. * testsuite/20_util/variant/compile.cc: Add tests. * testsuite/20_util/variant/hash.cc: Add tests. From-SVN: r243294 --- libstdc++-v3/ChangeLog | 8 ++ .../include/bits/enable_special_members.h | 5 +- libstdc++-v3/include/std/variant | 89 +++++++++++++------ .../testsuite/20_util/variant/compile.cc | 43 ++++++++- .../testsuite/20_util/variant/hash.cc | 4 + 5 files changed, 114 insertions(+), 35 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 8a3ab43d665..cdad9728b41 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,11 @@ +2016-12-07 Tim Shen + + * include/bits/enable_special_members.h: Make + _Enable_default_constructor constexpr. + * include/std/variant (variant::emplace, variant::swap, std::swap, + std::hash): Sfinae on emplace and std::swap; handle __poison_hash bases + of duplicated types. + 2016-12-07 Tim Shen * include/std/variant (std::get, operator==): Implement constexpr diff --git a/libstdc++-v3/include/bits/enable_special_members.h b/libstdc++-v3/include/bits/enable_special_members.h index 07c6c99ef85..4f4477bfb33 100644 --- a/libstdc++-v3/include/bits/enable_special_members.h +++ b/libstdc++-v3/include/bits/enable_special_members.h @@ -38,7 +38,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct _Enable_default_constructor_tag { - explicit _Enable_default_constructor_tag() = default; + explicit constexpr _Enable_default_constructor_tag() = default; }; /** @@ -118,7 +118,8 @@ template operator=(_Enable_default_constructor&&) noexcept = default; // Can be used in other ctors. - explicit _Enable_default_constructor(_Enable_default_constructor_tag) { } + constexpr explicit + _Enable_default_constructor(_Enable_default_constructor_tag) { } }; template diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index a961a058a56..fa1e6548e49 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -330,14 +330,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } template - constexpr void _M_destroy_impl(std::index_sequence<__indices...>) + constexpr void _M_reset_impl(std::index_sequence<__indices...>) { if (_M_index != variant_npos) _S_vtable<__indices...>[_M_index](*this); } + void _M_reset() + { + _M_reset_impl(std::index_sequence_for<_Types...>{}); + _M_index = variant_npos; + } + ~_Variant_storage() - { _M_destroy_impl(std::index_sequence_for<_Types...>{}); } + { _M_reset(); } _Variadic_union<_Types...> _M_u; size_t _M_index; @@ -354,6 +360,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_index(_Np) { } + void _M_reset() + { _M_index = variant_npos; } + _Variadic_union<_Types...> _M_u; size_t _M_index; }; @@ -436,6 +445,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return *this; } + void _M_destructive_move(_Variant_base&& __rhs) + { + this->~_Variant_base(); + __try + { + ::new (this) _Variant_base(std::move(__rhs)); + } + __catch (...) + { + this->_M_index = variant_npos; + __throw_exception_again; + } + } + _Variant_base& operator=(_Variant_base&& __rhs) noexcept(__and_..., @@ -453,16 +476,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } else { - this->~_Variant_base(); - __try - { - ::new (this) _Variant_base(std::move(__rhs)); - } - __catch (...) - { - this->_M_index = variant_npos; - __throw_exception_again; - } + _M_destructive_move(std::move(__rhs)); } return *this; } @@ -682,6 +696,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template + struct _Base_dedup : public _Tp { }; + + template + struct _Variant_hash_base; + + template + struct _Variant_hash_base, + std::index_sequence<__indices...>> + : _Base_dedup<__indices, __poison_hash>>... { }; + _GLIBCXX_END_NAMESPACE_VERSION } // namespace __variant } // namespace __detail @@ -858,8 +883,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return false; } template - inline enable_if_t<__and_..., - is_swappable<_Types>...>::value> + inline enable_if_t<(is_move_constructible_v<_Types> && ...) + && (is_swappable_v<_Types> && ...)> swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) { __lhs.swap(__rhs); } @@ -1028,25 +1053,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template - void emplace(_Args&&... __args) + enable_if_t && __exactly_once<_Tp>> + emplace(_Args&&... __args) { - static_assert(__exactly_once<_Tp>, - "T should occur for exactly once in alternatives"); this->emplace<__index_of<_Tp>>(std::forward<_Args>(__args)...); __glibcxx_assert(holds_alternative<_Tp>(*this)); } template - void emplace(initializer_list<_Up> __il, _Args&&... __args) + enable_if_t&, _Args...> + && __exactly_once<_Tp>> + emplace(initializer_list<_Up> __il, _Args&&... __args) { - static_assert(__exactly_once<_Tp>, - "T should occur for exactly once in alternatives"); this->emplace<__index_of<_Tp>>(__il, std::forward<_Args>(__args)...); __glibcxx_assert(holds_alternative<_Tp>(*this)); } template - void emplace(_Args&&... __args) + enable_if_t, + _Args...>> + emplace(_Args&&... __args) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); @@ -1065,7 +1091,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template - void emplace(initializer_list<_Up> __il, _Args&&... __args) + enable_if_t, + initializer_list<_Up>&, _Args...>> + emplace(initializer_list<_Up> __il, _Args&&... __args) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); @@ -1092,7 +1120,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void swap(variant& __rhs) noexcept(__and_<__is_nothrow_swappable<_Types>...>::value - && is_nothrow_move_assignable_v) + && is_nothrow_move_constructible_v) { if (this->index() == __rhs.index()) { @@ -1107,17 +1135,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } else if (!this->_M_valid()) { - *this = std::move(__rhs); + this->_M_destructive_move(std::move(__rhs)); + __rhs._M_reset(); } else if (!__rhs._M_valid()) { - __rhs = std::move(*this); + __rhs._M_destructive_move(std::move(*this)); + this->_M_reset(); } else { auto __tmp = std::move(__rhs); - __rhs = std::move(*this); - *this = std::move(__tmp); + __rhs._M_destructive_move(std::move(*this)); + this->_M_destructive_move(std::move(__tmp)); } } @@ -1253,14 +1283,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template struct hash> - : private __poison_hash>... + : private __detail::__variant::_Variant_hash_base< + variant<_Types...>, std::index_sequence_for<_Types...>> { using result_type = size_t; using argument_type = variant<_Types...>; size_t operator()(const variant<_Types...>& __t) const - noexcept((... && noexcept(hash>{}(std::declval<_Types>())))) + noexcept((is_nothrow_callable_v>(_Types)> && ...)) { if (!__t.valueless_by_exception()) { diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index ab8ada2e919..087a17cb952 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -51,6 +51,15 @@ struct DefaultNoexcept DefaultNoexcept& operator=(DefaultNoexcept&&) noexcept = default; }; +struct MoveCtorOnly +{ + MoveCtorOnly() noexcept = delete; + MoveCtorOnly(const DefaultNoexcept&) noexcept = delete; + MoveCtorOnly(DefaultNoexcept&&) noexcept { } + MoveCtorOnly& operator=(const DefaultNoexcept&) noexcept = delete; + MoveCtorOnly& operator=(DefaultNoexcept&&) noexcept = delete; +}; + struct nonliteral { nonliteral() { } @@ -237,9 +246,9 @@ static_assert( !std::is_swappable_v> ); void test_swap() { - variant a, b; - a.swap(b); - swap(a, b); + static_assert(is_swappable_v>, ""); + static_assert(is_swappable_v>, ""); + static_assert(!is_swappable_v>, ""); } void test_visit() @@ -385,7 +394,8 @@ void test_adl() variant v4{in_place_type, il, x}; } -void test_variant_alternative() { +void test_variant_alternative() +{ static_assert(is_same_v>, int>, ""); static_assert(is_same_v>, string>, ""); @@ -393,3 +403,28 @@ void test_variant_alternative() { static_assert(is_same_v>, volatile int>, ""); static_assert(is_same_v>, const volatile int>, ""); } + +template + constexpr auto has_type_emplace(int) -> decltype((declval().template emplace(), true)) + { return true; }; + +template + constexpr bool has_type_emplace(...) + { return false; }; + +template + constexpr auto has_index_emplace(int) -> decltype((declval().template emplace(), true)) + { return true; }; + +template + constexpr bool has_index_emplace(...) + { return false; }; + +void test_emplace() +{ + static_assert(has_type_emplace, int>(0), ""); + static_assert(!has_type_emplace, int>(0), ""); + static_assert(has_index_emplace, 0>(0), ""); + static_assert(!has_type_emplace, AllDeleted>(0), ""); + static_assert(!has_index_emplace, 0>(0), ""); +} diff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc index 38991ae1f83..64d053f712e 100644 --- a/libstdc++-v3/testsuite/20_util/variant/hash.cc +++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc @@ -29,6 +29,10 @@ template auto f(...) -> decltype(std::false_type()); static_assert(!decltype(f(0))::value, ""); +static_assert(!decltype(f>(0))::value, ""); +static_assert(!decltype(f>(0))::value, ""); +static_assert(decltype(f>(0))::value, ""); +static_assert(decltype(f>(0))::value, ""); int main() {