* [Patches] Add variant constexpr support for visit, comparisons and get @ 2016-11-27 5:38 Tim Shen 2016-11-30 16:27 ` Jonathan Wakely 0 siblings, 1 reply; 10+ messages in thread From: Tim Shen @ 2016-11-27 5:38 UTC (permalink / raw) To: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 710 bytes --] This 4-patch series contains the following in order: a.diff: Remove uses-allocator ctors. They are going away, and removing it reduces the maintenance burden from now on. b.diff: Add constexpr support for get<> and comparisons. This patch also involves small refactoring of _Variant_storage. c.diff: Fix some libc++ test failures. d.diff: Add constexpr support for visit. This patch also removes __storage, __get_alternative, and __reserved_type_map, since we don't need to support reference/void types for now. The underlying design doesn't change - we still use the vtable approach to achieve O(1) runtime cost even under -O0. Bootstrapped and tested for each of them. Thanks! -- Regards, Tim Shen [-- Attachment #2: a.diff --] [-- Type: text/x-patch, Size: 10514 bytes --] commit 638ecd4cf354d853bb12b089a356df99531f9afa Author: Tim Shen <timshen@google.com> Date: Thu Nov 24 00:56:08 2016 -0800 2016-11-26 Tim Shen <timshen@google.com> * include/std/variant (__erased_use_alloc_ctor, _Variant_base::_Variant_base, variant::variant): Remove uses-allocator related functions. * testsuite/20_util/variant/compile.cc: Remove related tests. * testsuite/20_util/variant/run.cc: Remove related tests. diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 34ad3fd..2d9303a 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -202,14 +202,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __erased_ctor(void* __lhs, void* __rhs) { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); } - template<typename _Alloc, typename _Lhs, typename _Rhs> - constexpr void - __erased_use_alloc_ctor(const _Alloc& __a, void* __lhs, void* __rhs) - { - __uses_allocator_construct(__a, static_cast<decay_t<_Lhs>*>(__lhs), - __get_alternative<_Rhs>(__rhs)); - } - // TODO: Find a potential chance to reuse this accross the project. template<typename _Tp> constexpr void @@ -353,47 +345,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _Storage(__i, std::forward<_Args>(__args)...), _M_index(_Np) { } - template<typename _Alloc> - _Variant_base(const _Alloc& __a, const _Variant_base& __rhs) - : _Storage(), _M_index(__rhs._M_index) - { - if (__rhs._M_valid()) - { - static constexpr void - (*_S_vtable[])(const _Alloc&, void*, void*) = - { &__erased_use_alloc_ctor<_Alloc, __storage<_Types>&, - const __storage<_Types>&>... }; - _S_vtable[__rhs._M_index](__a, _M_storage(), __rhs._M_storage()); - } - } - - template<typename _Alloc> - _Variant_base(const _Alloc& __a, _Variant_base&& __rhs) - : _Storage(), _M_index(__rhs._M_index) - { - if (__rhs._M_valid()) - { - static constexpr void - (*_S_vtable[])(const _Alloc&, void*, void*) = - { &__erased_use_alloc_ctor<_Alloc, __storage<_Types>&, - __storage<_Types>&&>... }; - _S_vtable[__rhs._M_index](__a, _M_storage(), __rhs._M_storage()); - } - } - - template<typename _Alloc, size_t _Np, typename... _Args> - constexpr explicit - _Variant_base(const _Alloc& __a, in_place_index_t<_Np>, - _Args&&... __args) - : _Storage(), _M_index(_Np) - { - using _Storage = - __storage<variant_alternative_t<_Np, variant<_Types...>>>; - __uses_allocator_construct(__a, static_cast<_Storage*>(_M_storage()), - std::forward<_Args>(__args)...); - __glibcxx_assert(_M_index == _Np); - } - _Variant_base& operator=(const _Variant_base& __rhs) { @@ -1026,84 +977,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Default_ctor_enabler(_Enable_default_constructor_tag{}) { __glibcxx_assert(index() == _Np); } - template<typename _Alloc, - typename = enable_if_t< - __is_uses_allocator_constructible_v<__to_type<0>, _Alloc>>> - variant(allocator_arg_t, const _Alloc& __a) - : variant(allocator_arg, __a, in_place_index<0>) - { } - - template<typename _Alloc, - typename = enable_if_t<__and_<__is_uses_allocator_constructible< - _Types, _Alloc, - add_lvalue_reference_t<add_const_t<_Types>>>...>::value>> - variant(allocator_arg_t, const _Alloc& __a, const variant& __rhs) - : _Base(__a, __rhs), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { } - - template<typename _Alloc, - typename = enable_if_t<__and_< - __is_uses_allocator_constructible< - _Types, _Alloc, add_rvalue_reference_t<_Types>>...>::value>> - variant(allocator_arg_t, const _Alloc& __a, variant&& __rhs) - : _Base(__a, std::move(__rhs)), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { } - - template<typename _Alloc, typename _Tp, - typename = enable_if_t< - __exactly_once<__accepted_type<_Tp&&>> - && __is_uses_allocator_constructible_v< - __accepted_type<_Tp&&>, _Alloc, _Tp&&> - && !is_same_v<decay_t<_Tp>, variant>, variant&>> - variant(allocator_arg_t, const _Alloc& __a, _Tp&& __t) - : variant(allocator_arg, __a, in_place_index<__accepted_index<_Tp&&>>, - std::forward<_Tp>(__t)) - { __glibcxx_assert(holds_alternative<__accepted_type<_Tp&&>>(*this)); } - - template<typename _Alloc, typename _Tp, typename... _Args, - typename = enable_if_t< - __exactly_once<_Tp> - && __is_uses_allocator_constructible_v< - _Tp, _Alloc, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_type_t<_Tp>, - _Args&&... __args) - : variant(allocator_arg, __a, in_place_index<__index_of<_Tp>>, - std::forward<_Args>(__args)...) - { __glibcxx_assert(holds_alternative<_Tp>(*this)); } - - template<typename _Alloc, typename _Tp, typename _Up, typename... _Args, - typename = enable_if_t< - __exactly_once<_Tp> - && __is_uses_allocator_constructible_v< - _Tp, _Alloc, initializer_list<_Up>&, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_type_t<_Tp>, - initializer_list<_Up> __il, _Args&&... __args) - : variant(allocator_arg, __a, in_place_index<__index_of<_Tp>>, __il, - std::forward<_Args>(__args)...) - { __glibcxx_assert(holds_alternative<_Tp>(*this)); } - - template<typename _Alloc, size_t _Np, typename... _Args, - typename = enable_if_t< - __is_uses_allocator_constructible_v< - __to_type<_Np>, _Alloc, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_index_t<_Np>, - _Args&&... __args) - : _Base(__a, in_place_index<_Np>, std::forward<_Args>(__args)...), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { __glibcxx_assert(index() == _Np); } - - template<typename _Alloc, size_t _Np, typename _Up, typename... _Args, - typename = enable_if_t< - __is_uses_allocator_constructible_v< - __to_type<_Np>, _Alloc, initializer_list<_Up>&, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_index_t<_Np>, - initializer_list<_Up> __il, _Args&&... __args) - : _Base(__a, in_place_index<_Np>, __il, std::forward<_Args>(__args)...), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { __glibcxx_assert(index() == _Np); } - ~variant() = default; variant& operator=(const variant&) = default; @@ -1293,10 +1166,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __detail::__variant::__get_storage(__variants)...); } - template<typename... _Types, typename _Alloc> - struct uses_allocator<variant<_Types...>, _Alloc> - : true_type { }; - template<typename... _Types> struct hash<variant<_Types...>> : private __poison_hash<remove_const_t<_Types>>... diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index e3330be..5bd4e70 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -117,31 +117,6 @@ void in_place_type_ctor() static_assert(!is_constructible_v<variant<string, string>, in_place_type_t<string>, const char*>, ""); } -void uses_alloc_ctors() -{ - std::allocator<char> alloc; - variant<int> a(allocator_arg, alloc); - static_assert(!is_constructible_v<variant<AllDeleted>, allocator_arg_t, std::allocator<char>>, ""); - { - variant<string, int> b(allocator_arg, alloc, "a"); - static_assert(!is_constructible_v<variant<string, string>, allocator_arg_t, std::allocator<char>, const char*>, ""); - } - { - variant<string, int> b(allocator_arg, alloc, in_place_index<0>, "a"); - variant<string, string> c(allocator_arg, alloc, in_place_index<1>, "a"); - } - { - variant<string, int> b(allocator_arg, alloc, in_place_index<0>, {'a'}); - variant<string, string> c(allocator_arg, alloc, in_place_index<1>, {'a'}); - } - { - variant<int, string, int> b(allocator_arg, alloc, in_place_type<string>, "a"); - } - { - variant<int, string, int> b(allocator_arg, alloc, in_place_type<string>, {'a'}); - } -} - void dtor() { static_assert(is_destructible_v<variant<int, string>>, ""); @@ -309,9 +284,7 @@ namespace adl_trap void test_adl() { using adl_trap::X; - using std::allocator_arg; X x; - std::allocator<int> a; std::initializer_list<int> il; adl_trap::Visitor vis; @@ -324,11 +297,6 @@ void test_adl() variant<X> v2{in_place_type<X>, x}; variant<X> v3{in_place_index<0>, il, x}; variant<X> v4{in_place_type<X>, il, x}; - variant<X> v5{allocator_arg, a, in_place_index<0>, x}; - variant<X> v6{allocator_arg, a, in_place_type<X>, x}; - variant<X> v7{allocator_arg, a, in_place_index<0>, il, x}; - variant<X> v8{allocator_arg, a, in_place_type<X>, il, x}; - variant<X> v9{allocator_arg, a, in_place_type<X>, 1}; } void test_variant_alternative() { diff --git a/libstdc++-v3/testsuite/20_util/variant/run.cc b/libstdc++-v3/testsuite/20_util/variant/run.cc index 71e0176..fb5d7c4 100644 --- a/libstdc++-v3/testsuite/20_util/variant/run.cc +++ b/libstdc++-v3/testsuite/20_util/variant/run.cc @@ -160,48 +160,6 @@ void in_place_type_ctor() } } -struct UsesAllocatable -{ - template<typename Alloc> - UsesAllocatable(std::allocator_arg_t, const Alloc& a) - : d(0), a(static_cast<const void*>(&a)) { } - - template<typename Alloc> - UsesAllocatable(std::allocator_arg_t, const Alloc& a, const UsesAllocatable&) - : d(1), a(static_cast<const void*>(&a)) { } - - template<typename Alloc> - UsesAllocatable(std::allocator_arg_t, const Alloc& a, UsesAllocatable&&) - : d(2), a(static_cast<const void*>(&a)) { } - - int d; - const void* a; -}; - -namespace std -{ - template<> - struct uses_allocator<UsesAllocatable, std::allocator<char>> : true_type { }; -} - -void uses_allocator_ctor() -{ - std::allocator<char> a; - variant<UsesAllocatable> v(std::allocator_arg, a); - VERIFY(get<0>(v).d == 0); - VERIFY(get<0>(v).a == &a); - { - variant<UsesAllocatable> u(std::allocator_arg, a, v); - VERIFY(get<0>(u).d == 1); - VERIFY(get<0>(u).a == &a); - } - { - variant<UsesAllocatable> u(std::allocator_arg, a, std::move(v)); - VERIFY(get<0>(u).d == 2); - VERIFY(get<0>(u).a == &a); - } -} - void emplace() { variant<int, string> v; @@ -450,7 +408,6 @@ int main() arbitrary_ctor(); in_place_index_ctor(); in_place_type_ctor(); - uses_allocator_ctor(); copy_assign(); move_assign(); arbitrary_assign(); [-- Attachment #3: b.diff --] [-- Type: text/x-patch, Size: 28546 bytes --] commit 7c510c3d9ac56684a966ae818a2b0d64d63463c2 Author: Tim Shen <timshen@google.com> Date: Thu Nov 24 12:00:48 2016 -0800 2016-11-26 Tim Shen <timshen@google.com> * include/std/variant: Implement constexpr comparison and get<>. * testsuite/20_util/variant/compile.cc: Tests. diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 2d9303a..a913074 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -154,31 +154,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Alternative> using __storage = _Alternative; - template<typename _Type, bool __is_literal = std::is_literal_type_v<_Type>> + // _Uninitialized nullify the destructor calls. + // This is necessary, since we define _Variadic_union as a recursive union, + // and we don't want to inspect the union members one by one in its dtor, + // it's slow. + template<typename _Type, bool = std::is_literal_type_v<_Type>> struct _Uninitialized; template<typename _Type> struct _Uninitialized<_Type, true> { - constexpr _Uninitialized() = default; - template<typename... _Args> constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) : _M_storage(std::forward<_Args>(__args)...) { } + constexpr const _Type& _M_get() const & + { return _M_storage; } + + constexpr _Type& _M_get() & + { return _M_storage; } + + constexpr const _Type&& _M_get() const && + { return std::move(_M_storage); } + + constexpr _Type&& _M_get() && + { return std::move(_M_storage); } + _Type _M_storage; }; template<typename _Type> struct _Uninitialized<_Type, false> { - constexpr _Uninitialized() = default; - template<typename... _Args> constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); } + const _Type& _M_get() const & + { + return *static_cast<const _Type*>( + static_cast<const void*>(&_M_storage)); + } + + _Type& _M_get() & + { return *static_cast<_Type*>(static_cast<void*>(&_M_storage)); } + + const _Type&& _M_get() const && + { + return std::move(*static_cast<const _Type*>( + static_cast<const void*>(&_M_storage))); + } + + _Type&& _M_get() && + { + return std::move(*static_cast<_Type*>(static_cast<void*>(&_M_storage))); + } + typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type _M_storage; }; @@ -195,6 +227,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION *static_cast<_Storage*>(__ptr)); } + template<typename _Union> + constexpr decltype(auto) __get(in_place_index_t<0>, _Union&& __u) + { return std::forward<_Union>(__u)._M_first._M_get(); } + + template<size_t _Np, typename _Union> + constexpr decltype(auto) __get(in_place_index_t<_Np>, _Union&& __u) + { return __get(in_place_index<_Np-1>, std::forward<_Union>(__u)._M_rest); } + + // Returns the typed storage for __v. + template<size_t _Np, typename _Variant> + constexpr decltype(auto) __get(_Variant&& __v) + { + return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u); + } + // Various functions as "vtable" entries, where those vtables are used by // polymorphic operations. template<typename _Lhs, typename _Rhs> @@ -202,13 +249,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __erased_ctor(void* __lhs, void* __rhs) { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); } - // TODO: Find a potential chance to reuse this accross the project. - template<typename _Tp> + template<typename _Variant, size_t _Np> constexpr void - __erased_dtor(void* __ptr) + __erased_dtor(_Variant&& __v) { - using _Storage = decay_t<_Tp>; - static_cast<_Storage*>(__ptr)->~_Storage(); + auto&& __element = __get<_Np>(std::forward<_Variant>(__v)); + using _Type = std::remove_reference_t<decltype(__element)>; + __element.~_Type(); } template<typename _Lhs, typename _Rhs> @@ -224,90 +271,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs)); } - template<typename _Lhs, typename _Rhs> + template<typename _Variant, size_t _Np> constexpr bool - __erased_equal_to(void* __lhs, void* __rhs) - { return __get_alternative<_Lhs>(__lhs) == __get_alternative<_Rhs>(__rhs); } + __erased_equal_to(_Variant&& __lhs, _Variant&& __rhs) + { + return __get<_Np>(std::forward<_Variant>(__lhs)) + == __get<_Np>(std::forward<_Variant>(__rhs)); + } - template<typename _Lhs, typename _Rhs> + template<typename _Variant, size_t _Np> constexpr bool - __erased_less_than(void* __lhs, void* __rhs) - { return __get_alternative<_Lhs>(__lhs) < __get_alternative<_Rhs>(__rhs); } + __erased_less_than(const _Variant& __lhs, const _Variant& __rhs) + { + return __get<_Np>(std::forward<_Variant>(__lhs)) + < __get<_Np>(std::forward<_Variant>(__rhs)); + } template<typename _Tp> constexpr size_t __erased_hash(void* __t) { return std::hash<decay_t<_Tp>>{}(__get_alternative<_Tp>(__t)); } + // Defines members and ctors. template<typename... _Types> - struct _Variant_base; + union _Variadic_union { }; - template<typename... _Types> - struct _Variant_storage - { constexpr _Variant_storage() = default; }; - - // Use recursive unions to implement a trivially destructible variant. template<typename _First, typename... _Rest> - struct _Variant_storage<_First, _Rest...> + union _Variadic_union<_First, _Rest...> { - constexpr _Variant_storage() = default; + constexpr _Variadic_union() : _M_rest() { } + + template<typename... _Args> + constexpr _Variadic_union(in_place_index_t<0>, _Args&&... __args) + : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) + { } + + template<size_t _Np, typename... _Args> + constexpr _Variadic_union(in_place_index_t<_Np>, _Args&&... __args) + : _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...) + { } + + _Uninitialized<_First> _M_first; + _Variadic_union<_Rest...> _M_rest; + }; + + // Defines index and the dtor, possibly trivial. + template<bool __trivially_destructible, typename... _Types> + struct _Variant_storage; + + template<typename... _Types> + struct _Variant_storage<false, _Types...> + { + template<size_t... __indices> + static constexpr void (*_S_vtable[])(const _Variant_storage&) = + { &__erased_dtor<const _Variant_storage&, __indices>... }; + + constexpr _Variant_storage() : _M_index(variant_npos) { } template<size_t _Np, typename... _Args> constexpr _Variant_storage(in_place_index_t<_Np>, _Args&&... __args) - : _M_union(in_place_index<_Np>, std::forward<_Args>(__args)...) + : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...), + _M_index(_Np) { } - ~_Variant_storage() = default; + template<size_t... __indices> + constexpr void _M_destroy_impl(std::index_sequence<__indices...>) + { + if (_M_index != variant_npos) + _S_vtable<__indices...>[_M_index](*this); + } - constexpr void* - _M_storage() const - { - return const_cast<void*>( - static_cast<const void*>(std::addressof(_M_union._M_first._M_storage))); - } + ~_Variant_storage() + { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } - union _Union - { - constexpr _Union() {}; - - template<typename... _Args> - constexpr _Union(in_place_index_t<0>, _Args&&... __args) - : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) - { } - - template<size_t _Np, typename... _Args, - typename = enable_if_t<0 < _Np && _Np < sizeof...(_Rest) + 1>> - constexpr _Union(in_place_index_t<_Np>, _Args&&... __args) - : _M_rest(in_place_index<_Np - 1>, std::forward<_Args>(__args)...) - { } - - _Uninitialized<__storage<_First>> _M_first; - _Variant_storage<_Rest...> _M_rest; - } _M_union; + _Variadic_union<_Types...> _M_u; + size_t _M_index; }; - template<typename _Derived, bool __is_trivially_destructible> - struct _Dtor_mixin + template<typename... _Types> + struct _Variant_storage<true, _Types...> { - ~_Dtor_mixin() - { static_cast<_Derived*>(this)->_M_destroy(); } - }; + constexpr _Variant_storage() : _M_index(variant_npos) { } - template<typename _Derived> - struct _Dtor_mixin<_Derived, true> - { - ~_Dtor_mixin() = default; + template<size_t _Np, typename... _Args> + constexpr _Variant_storage(in_place_index_t<_Np>, _Args&&... __args) + : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...), + _M_index(_Np) + { } + + _Variadic_union<_Types...> _M_u; + size_t _M_index; }; // Helps SFINAE on special member functions. Otherwise it can live in variant // class. template<typename... _Types> struct _Variant_base : - _Variant_storage<_Types...>, - _Dtor_mixin<_Variant_base<_Types...>, - __and_<std::is_trivially_destructible<_Types>...>::value> + _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...), + _Types...> { - using _Storage = _Variant_storage<_Types...>; + using _Storage = + _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...), + _Types...>; constexpr _Variant_base() @@ -316,7 +381,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _Variant_base(in_place_index<0>) { } _Variant_base(const _Variant_base& __rhs) - : _Storage(), _M_index(__rhs._M_index) { if (__rhs._M_valid()) { @@ -324,31 +388,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { &__erased_ctor<__storage<_Types>&, const __storage<_Types>&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); + this->_M_index = __rhs._M_index; } } _Variant_base(_Variant_base&& __rhs) noexcept(__and_<is_nothrow_move_constructible<_Types>...>::value) - : _Storage(), _M_index(__rhs._M_index) { if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = { &__erased_ctor<__storage<_Types>&, __storage<_Types>&&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); + this->_M_index = __rhs._M_index; } } template<size_t _Np, typename... _Args> constexpr explicit _Variant_base(in_place_index_t<_Np> __i, _Args&&... __args) - : _Storage(__i, std::forward<_Args>(__args)...), _M_index(_Np) + : _Storage(__i, std::forward<_Args>(__args)...) { } _Variant_base& operator=(const _Variant_base& __rhs) { - if (_M_index == __rhs._M_index) + if (this->_M_index == __rhs._M_index) { if (__rhs._M_valid()) { @@ -368,11 +433,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } __catch (...) { - _M_index = variant_npos; + this->_M_index = variant_npos; __throw_exception_again; } } - __glibcxx_assert(_M_index == __rhs._M_index); + __glibcxx_assert(this->_M_index == __rhs._M_index); return *this; } @@ -381,7 +446,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(__and_<is_nothrow_move_constructible<_Types>..., is_nothrow_move_assignable<_Types>...>::value) { - if (_M_index == __rhs._M_index) + if (this->_M_index == __rhs._M_index) { if (__rhs._M_valid()) { @@ -400,32 +465,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } __catch (...) { - _M_index = variant_npos; + this->_M_index = variant_npos; __throw_exception_again; } } return *this; } - void _M_destroy() + void* + _M_storage() const { - if (_M_valid()) - { - static constexpr void (*_S_vtable[])(void*) = - { &__erased_dtor<__storage<_Types>&>... }; - _S_vtable[this->_M_index](_M_storage()); - } + return const_cast<void*>(static_cast<const void*>( + std::addressof(_Storage::_M_u))); } - constexpr void* - _M_storage() const - { return _Storage::_M_storage(); } - constexpr bool _M_valid() const noexcept - { return _M_index != variant_npos; } - - size_t _M_index; + { return this->_M_index != variant_npos; } }; // For how many times does _Tp appear in _Tuple? @@ -490,15 +546,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void* __get_storage(_Variant&& __v) { return __v._M_storage(); } - // Returns the reference to the desired alternative. - // It is as unsafe as a reinterpret_cast. - template<typename _Tp, typename _Variant> - decltype(auto) __access(_Variant&& __v) - { - return __get_alternative<__reserved_type_map<_Variant&&, __storage<_Tp>>>( - __get_storage(std::forward<_Variant>(__v))); - } - // A helper used to create variadic number of _To types. template<typename _From, typename _To> using _To_type = _To; @@ -598,9 +645,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>) { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); } - template<size_t __index> + template<size_t __index, typename T> static constexpr void - _S_apply_single_alt(auto& __element) + _S_apply_single_alt(T& __element) { using _Alternative = variant_alternative_t<__index, decay_t<_First>>; using _Qualified_storage = __reserved_type_map< @@ -656,23 +703,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>& + constexpr variant_alternative_t<_Np, variant<_Types...>>& get(variant<_Types...>&); template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>&& + constexpr variant_alternative_t<_Np, variant<_Types...>>&& get(variant<_Types...>&&); template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>> const& + constexpr variant_alternative_t<_Np, variant<_Types...>> const& get(const variant<_Types...>&); template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>> const&& + constexpr variant_alternative_t<_Np, variant<_Types...>> const&& get(const variant<_Types...>&&); template<typename _Tp, typename... _Types> - inline _Tp& get(variant<_Types...>& __v) + constexpr inline _Tp& get(variant<_Types...>& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -681,7 +728,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline _Tp&& get(variant<_Types...>&& __v) + constexpr inline _Tp&& get(variant<_Types...>&& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -691,7 +738,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline const _Tp& get(const variant<_Types...>& __v) + constexpr inline const _Tp& get(const variant<_Types...>& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -700,7 +747,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline const _Tp&& get(const variant<_Types...>&& __v) + constexpr inline const _Tp&& get(const variant<_Types...>&& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -710,7 +757,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<size_t _Np, typename... _Types> - inline add_pointer_t<variant_alternative_t<_Np, variant<_Types...>>> + constexpr inline + add_pointer_t<variant_alternative_t<_Np, variant<_Types...>>> get_if(variant<_Types...>* __ptr) noexcept { using _Alternative_type = variant_alternative_t<_Np, variant<_Types...>>; @@ -718,12 +766,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "The index should be in [0, number of alternatives)"); static_assert(!is_void_v<_Alternative_type>, "_Tp should not be void"); if (__ptr && __ptr->index() == _Np) - return &__detail::__variant::__access<_Alternative_type>(*__ptr); + return &__detail::__variant::__get<_Np>(*__ptr); return nullptr; } template<size_t _Np, typename... _Types> - inline add_pointer_t<const variant_alternative_t<_Np, variant<_Types...>>> + constexpr inline + add_pointer_t<const variant_alternative_t<_Np, variant<_Types...>>> get_if(const variant<_Types...>* __ptr) noexcept { using _Alternative_type = variant_alternative_t<_Np, variant<_Types...>>; @@ -731,12 +780,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "The index should be in [0, number of alternatives)"); static_assert(!is_void_v<_Alternative_type>, "_Tp should not be void"); if (__ptr && __ptr->index() == _Np) - return &__detail::__variant::__access<_Alternative_type>(*__ptr); + return &__detail::__variant::__get<_Np>(*__ptr); return nullptr; } template<typename _Tp, typename... _Types> - inline add_pointer_t<_Tp> get_if(variant<_Types...>* __ptr) noexcept + constexpr inline add_pointer_t<_Tp> + get_if(variant<_Types...>* __ptr) noexcept { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -745,7 +795,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline add_pointer_t<const _Tp> get_if(const variant<_Types...>* __ptr) + constexpr inline add_pointer_t<const _Tp> + get_if(const variant<_Types...>* __ptr) noexcept { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, @@ -755,64 +806,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename... _Types> - bool operator==(const variant<_Types...>& __lhs, - const variant<_Types...>& __rhs) + constexpr bool operator==(const variant<_Types...>& __lhs, + const variant<_Types...>& __rhs) { - if (__lhs.index() != __rhs.index()) - return false; - - if (__lhs.valueless_by_exception()) - return true; - - using __detail::__variant::__storage; - static constexpr bool (*_S_vtable[])(void*, void*) = - { &__detail::__variant::__erased_equal_to< - const __storage<_Types>&, const __storage<_Types>&>... }; - return _S_vtable[__lhs.index()]( - __detail::__variant::__get_storage(__lhs), - __detail::__variant::__get_storage(__rhs)); + return __lhs._M_equal_to(__rhs, + std::make_index_sequence<sizeof...(_Types)>{}); } template<typename... _Types> - inline bool + constexpr inline bool operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs == __rhs); } template<typename... _Types> - inline bool + constexpr inline bool operator<(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { - if (__lhs.index() < __rhs.index()) - return true; - - if (__lhs.index() > __rhs.index()) - return false; - - if (__lhs.valueless_by_exception()) - return false; - - using __detail::__variant::__storage; - static constexpr bool (*_S_vtable[])(void*, void*) = - { &__detail::__variant::__erased_less_than< - const __storage<_Types>&, - const __storage<_Types>&>... }; - return _S_vtable[__lhs.index()]( - __detail::__variant::__get_storage(__lhs), - __detail::__variant::__get_storage(__rhs)); + return __lhs._M_less_than(__rhs, + std::make_index_sequence<sizeof...(_Types)>{}); } template<typename... _Types> - inline bool + constexpr inline bool operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return __rhs < __lhs; } template<typename... _Types> - inline bool + constexpr inline bool operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs > __rhs); } template<typename... _Types> - inline bool + constexpr inline bool operator>=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs < __rhs); } @@ -1096,60 +1121,120 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } + private: + template<size_t... __indices> + static constexpr bool + (*_S_equal_to_vtable[])(const variant&, const variant&) = + { &__detail::__variant::__erased_equal_to< + const variant&, __indices>... }; + + template<size_t... __indices> + static constexpr bool + (*_S_less_than_vtable[])(const variant&, const variant&) = + { &__detail::__variant::__erased_less_than< + const variant&, __indices>... }; + + template<size_t... __indices> + constexpr bool + _M_equal_to(const variant& __rhs, + std::index_sequence<__indices...>) const + { + if (this->index() != __rhs.index()) + return false; + + if (this->valueless_by_exception()) + return true; + + return _S_equal_to_vtable<__indices...>[this->index()](*this, __rhs); + } + + template<size_t... __indices> + constexpr inline bool + _M_less_than(const variant& __rhs, + std::index_sequence<__indices...>) const + { + auto __lhs_index = this->index(); + auto __rhs_index = __rhs.index(); + + if (__lhs_index < __rhs_index) + return true; + + if (__lhs_index > __rhs_index) + return false; + + if (this->valueless_by_exception()) + return false; + + return _S_less_than_vtable<__indices...>[__lhs_index](*this, __rhs); + } + + template<size_t _Np, typename _Vp> + friend constexpr decltype(auto) __detail::__variant:: +#if _GLIBCXX_INLINE_VERSION + __7:: // Required due to PR c++/59256 +#endif + __get(_Vp&& __v); + template<typename _Vp> friend void* __detail::__variant:: #if _GLIBCXX_INLINE_VERSION __7:: // Required due to PR c++/59256 #endif __get_storage(_Vp&& __v); + + template<typename... _Tp> + friend constexpr bool + operator==(const variant<_Tp...>& __lhs, + const variant<_Tp...>& __rhs); + + template<typename... _Tp> + friend constexpr bool + operator<(const variant<_Tp...>& __lhs, + const variant<_Tp...>& __rhs); }; template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>& + constexpr variant_alternative_t<_Np, variant<_Types...>>& get(variant<_Types...>& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(__v); + return __detail::__variant::__get<_Np>(__v); } template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>&& + constexpr variant_alternative_t<_Np, variant<_Types...>>&& get(variant<_Types...>&& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(std::move(__v)); + return __detail::__variant::__get<_Np>(std::move(__v)); } template<size_t _Np, typename... _Types> - const variant_alternative_t<_Np, variant<_Types...>>& + constexpr const variant_alternative_t<_Np, variant<_Types...>>& get(const variant<_Types...>& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(__v); + return __detail::__variant::__get<_Np>(__v); } template<size_t _Np, typename... _Types> - const variant_alternative_t<_Np, variant<_Types...>>&& + constexpr const variant_alternative_t<_Np, variant<_Types...>>&& get(const variant<_Types...>&& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(std::move(__v)); + return __detail::__variant::__get<_Np>(std::move(__v)); } template<typename _Visitor, typename... _Variants> diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index 5bd4e70..54acc93 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -51,6 +51,14 @@ struct DefaultNoexcept DefaultNoexcept& operator=(DefaultNoexcept&&) noexcept = default; }; +struct nonliteral +{ + nonliteral() { } + + bool operator<(const nonliteral&) const; + bool operator==(const nonliteral&) const; +}; + void default_ctor() { static_assert(is_default_constructible_v<variant<int, string>>, ""); @@ -175,22 +183,40 @@ void test_get() void test_relational() { { - const variant<int, string> a, b; - (void)(a < b); - (void)(a > b); - (void)(a <= b); - (void)(a == b); - (void)(a != b); - (void)(a >= b); + constexpr variant<int, nonliteral> a(42), b(43); + static_assert((a < b), ""); + static_assert(!(a > b), ""); + static_assert((a <= b), ""); + static_assert(!(a == b), ""); + static_assert((a != b), ""); + static_assert(!(a >= b), ""); } { - const monostate a, b; - (void)(a < b); - (void)(a > b); - (void)(a <= b); - (void)(a == b); - (void)(a != b); - (void)(a >= b); + constexpr variant<int, nonliteral> a(42), b(42); + static_assert(!(a < b), ""); + static_assert(!(a > b), ""); + static_assert((a <= b), ""); + static_assert((a == b), ""); + static_assert(!(a != b), ""); + static_assert((a >= b), ""); + } + { + constexpr variant<int, nonliteral> a(43), b(42); + static_assert(!(a < b), ""); + static_assert((a > b), ""); + static_assert(!(a <= b), ""); + static_assert(!(a == b), ""); + static_assert((a != b), ""); + static_assert((a >= b), ""); + } + { + constexpr monostate a, b; + static_assert(!(a < b), ""); + static_assert(!(a > b), ""); + static_assert((a <= b), ""); + static_assert((a == b), ""); + static_assert(!(a != b), ""); + static_assert((a >= b), ""); } } @@ -247,14 +273,59 @@ void test_constexpr() constexpr literal() = default; }; - struct nonliteral { - nonliteral() { } - }; - constexpr variant<literal, nonliteral> v{}; constexpr variant<literal, nonliteral> v1{in_place_type<literal>}; constexpr variant<literal, nonliteral> v2{in_place_index<0>}; } + + { + constexpr variant<int> a(42); + static_assert(get<0>(a) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<0>(a) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<1>(a) == 42, ""); + } + { + constexpr variant<int> a(42); + static_assert(get<int>(a) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<int>(a) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<int>(a) == 42, ""); + } + { + constexpr variant<int> a(42); + static_assert(get<0>(std::move(a)) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<0>(std::move(a)) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<1>(std::move(a)) == 42, ""); + } + { + constexpr variant<int> a(42); + static_assert(get<int>(std::move(a)) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<int>(std::move(a)) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<int>(std::move(a)) == 42, ""); + } } void test_pr77641() [-- Attachment #4: c.diff --] [-- Type: text/x-patch, Size: 10854 bytes --] commit e80deb97ce75f6a3057c1115c4511fa3dbfb08d9 Author: Tim Shen <timshen@google.com> Date: Thu Nov 24 13:35:00 2016 -0800 2016-11-27 Tim Shen <timshen@google.com> * 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. diff --git a/libstdc++-v3/include/bits/enable_special_members.h b/libstdc++-v3/include/bits/enable_special_members.h index 07c6c99..4f4477b 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<typename _Tag> 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<typename _Tag> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index a913074..7cc7402 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -335,14 +335,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } template<size_t... __indices> - 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::make_index_sequence<sizeof...(_Types)>{}); + _M_index = variant_npos; + } + ~_Variant_storage() - { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } + { _M_reset(); } _Variadic_union<_Types...> _M_u; size_t _M_index; @@ -359,6 +365,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_index(_Np) { } + void _M_reset() + { _M_index = variant_npos; } + _Variadic_union<_Types...> _M_u; size_t _M_index; }; @@ -441,6 +450,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_<is_nothrow_move_constructible<_Types>..., @@ -458,16 +481,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; } @@ -687,6 +701,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; + template<size_t _Np, typename _Tp> + struct _Base_dedup : public _Tp { }; + + template<typename _Variant, typename __indices> + struct _Variant_hash_base; + + template<typename... _Types, size_t... __indices> + struct _Variant_hash_base<variant<_Types...>, + std::index_sequence<__indices...>> + : _Base_dedup<__indices, __poison_hash<remove_const_t<_Types>>>... { }; + _GLIBCXX_END_NAMESPACE_VERSION } // namespace __variant } // namespace __detail @@ -865,8 +890,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return false; } template<typename... _Types> - inline auto swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) - noexcept(noexcept(__lhs.swap(__rhs))) -> decltype(__lhs.swap(__rhs)) + 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); } class bad_variant_access : public exception @@ -1028,25 +1055,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Args> - void emplace(_Args&&... __args) + enable_if_t<is_constructible_v<_Tp, _Args...> && __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<typename _Tp, typename _Up, typename... _Args> - void emplace(initializer_list<_Up> __il, _Args&&... __args) + enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _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<size_t _Np, typename... _Args> - void emplace(_Args&&... __args) + enable_if_t<is_constructible_v<variant_alternative_t<_Np, variant>, + _Args...>> + emplace(_Args&&... __args) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); @@ -1065,7 +1093,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<size_t _Np, typename _Up, typename... _Args> - void emplace(initializer_list<_Up> __il, _Args&&... __args) + enable_if_t<is_constructible_v<variant_alternative_t<_Np, variant>, + 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 +1122,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void swap(variant& __rhs) noexcept(__and_<__is_nothrow_swappable<_Types>...>::value - && is_nothrow_move_assignable_v<variant>) + && is_nothrow_move_constructible_v<variant>) { if (this->index() == __rhs.index()) { @@ -1107,17 +1137,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 +1285,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename... _Types> struct hash<variant<_Types...>> - : private __poison_hash<remove_const_t<_Types>>... + : private __detail::__variant::_Variant_hash_base< + variant<_Types...>, std::make_index_sequence<sizeof...(_Types)>> { using result_type = size_t; using argument_type = variant<_Types...>; size_t operator()(const variant<_Types...>& __t) const - noexcept((... && noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())))) + noexcept((noexcept(hash<decay_t<_Types>>{}(std::declval<_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 54acc93..b973143 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() { } @@ -222,9 +231,9 @@ void test_relational() void test_swap() { - variant<int, string> a, b; - a.swap(b); - swap(a, b); + static_assert(is_swappable_v<variant<int, string>>, ""); + static_assert(is_swappable_v<variant<MoveCtorOnly>>, ""); + static_assert(!is_swappable_v<variant<AllDeleted>>, ""); } void test_visit() @@ -370,7 +379,8 @@ void test_adl() variant<X> v4{in_place_type<X>, il, x}; } -void test_variant_alternative() { +void test_variant_alternative() +{ static_assert(is_same_v<variant_alternative_t<0, variant<int, string>>, int>, ""); static_assert(is_same_v<variant_alternative_t<1, variant<int, string>>, string>, ""); @@ -378,3 +388,28 @@ void test_variant_alternative() { static_assert(is_same_v<variant_alternative_t<0, volatile variant<int>>, volatile int>, ""); static_assert(is_same_v<variant_alternative_t<0, const volatile variant<int>>, const volatile int>, ""); } + +template<typename V, typename T> + constexpr auto has_type_emplace(int) -> decltype((declval<V>().template emplace<T>(), true)) + { return true; }; + +template<typename V, typename T> + constexpr bool has_type_emplace(...) + { return false; }; + +template<typename V, size_t N> + constexpr auto has_index_emplace(int) -> decltype((declval<V>().template emplace<N>(), true)) + { return true; }; + +template<typename V, size_t T> + constexpr bool has_index_emplace(...) + { return false; }; + +void test_emplace() +{ + static_assert(has_type_emplace<variant<int>, int>(0), ""); + static_assert(!has_type_emplace<variant<long>, int>(0), ""); + static_assert(has_index_emplace<variant<int>, 0>(0), ""); + static_assert(!has_type_emplace<variant<AllDeleted>, AllDeleted>(0), ""); + static_assert(!has_index_emplace<variant<AllDeleted>, 0>(0), ""); +} diff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc index 38991ae..64d053f 100644 --- a/libstdc++-v3/testsuite/20_util/variant/hash.cc +++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc @@ -29,6 +29,10 @@ template<class T> auto f(...) -> decltype(std::false_type()); static_assert(!decltype(f<S>(0))::value, ""); +static_assert(!decltype(f<std::variant<S>>(0))::value, ""); +static_assert(!decltype(f<std::variant<S, S>>(0))::value, ""); +static_assert(decltype(f<std::variant<int>>(0))::value, ""); +static_assert(decltype(f<std::variant<int, int>>(0))::value, ""); int main() { [-- Attachment #5: d.diff --] [-- Type: text/x-patch, Size: 19759 bytes --] commit 1503eff1772d0ad7638dad2507d860ea56039714 Author: Tim Shen <timshen@google.com> Date: Sat Nov 26 20:10:40 2016 -0800 2016-11-27 Tim Shen <timshen@google.com> * include/std/variant (visit): Make visit constexpr. Also cleanup __get_alternative and __storage, since we don't support reference/void alternatives any more. * testsuite/20_util/variant/compile.cc: Add tests. diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 7cc7402..1159772 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -42,10 +42,33 @@ #include <bits/move.h> #include <bits/uses_allocator.h> #include <bits/functional_hash.h> +#include <bits/invoke.h> namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +namespace __detail +{ +namespace __variant +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<size_t _Np, typename... _Types> + struct _Nth_type; + + template<size_t _Np, typename _First, typename... _Rest> + struct _Nth_type<_Np, _First, _Rest...> + : _Nth_type<_Np-1, _Rest...> { }; + + template<typename _First, typename... _Rest> + struct _Nth_type<0, _First, _Rest...> + { using type = _First; }; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace __variant +} // namespace __detail + +_GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename... _Types> class tuple; template<typename... _Types> class variant; @@ -99,6 +122,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr size_t variant_npos = -1; + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>>& + get(variant<_Types...>&); + + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>>&& + get(variant<_Types...>&&); + + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>> const& + get(const variant<_Types...>&); + + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>> const&& + get(const variant<_Types...>&&); + _GLIBCXX_END_NAMESPACE_VERSION namespace __detail @@ -119,41 +158,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::integral_constant<size_t, is_same_v<_Tp, _First> ? 0 : __index_of_v<_Tp, _Rest...> + 1> {}; - // Extract _From's qualifiers and references and apply it to _To. - // __reserved_type_map<const int&, char> is const char&. - template<typename _From, typename _To> - struct __reserved_type_map_impl - { using type = _To; }; - - template<typename _From, typename _To> - using __reserved_type_map = - typename __reserved_type_map_impl<_From, _To>::type; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<_From&, _To> - { using type = add_lvalue_reference_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<_From&&, _To> - { using type = add_rvalue_reference_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<const _From, _To> - { using type = add_const_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<volatile _From, _To> - { using type = add_volatile_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<const volatile _From, _To> - { using type = add_cv_t<__reserved_type_map<_From, _To>>; }; - - // This abstraction might be useful for future features, - // e.g. boost::recursive_wrapper. - template<typename _Alternative> - using __storage = _Alternative; - // _Uninitialized nullify the destructor calls. // This is necessary, since we define _Variadic_union as a recursive union, // and we don't want to inspect the union members one by one in its dtor, @@ -215,16 +219,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_storage; }; - // Given a qualified storage type, return the desired reference. - // For example, variant<int>&& stores the int as __storage<int>, and - // _Qualified_storage will be __storage<int>&&. - template<typename _Qualified_storage> - decltype(auto) - __get_alternative(void* __ptr) + template<typename _Ref> + _Ref __ref_cast(void* __ptr) { - using _Storage = decay_t<_Qualified_storage>; - return __reserved_type_map<_Qualified_storage, _Storage>( - *static_cast<_Storage*>(__ptr)); + return static_cast<_Ref>(*static_cast<remove_reference_t<_Ref>*>(__ptr)); } template<typename _Union> @@ -247,7 +245,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Lhs, typename _Rhs> constexpr void __erased_ctor(void* __lhs, void* __rhs) - { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); } + { ::new (__lhs) remove_reference_t<_Lhs>(__ref_cast<_Rhs>(__rhs)); } template<typename _Variant, size_t _Np> constexpr void @@ -261,14 +259,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Lhs, typename _Rhs> constexpr void __erased_assign(void* __lhs, void* __rhs) - { __get_alternative<_Lhs>(__lhs) = __get_alternative<_Rhs>(__rhs); } + { __ref_cast<_Lhs>(__lhs) = __ref_cast<_Rhs>(__rhs); } template<typename _Lhs, typename _Rhs> constexpr void __erased_swap(void* __lhs, void* __rhs) { using std::swap; - swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs)); + swap(__ref_cast<_Lhs>(__lhs), __ref_cast<_Rhs>(__rhs)); } template<typename _Variant, size_t _Np> @@ -290,7 +288,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> constexpr size_t __erased_hash(void* __t) - { return std::hash<decay_t<_Tp>>{}(__get_alternative<_Tp>(__t)); } + { + return std::hash<remove_cv_t<remove_reference_t<_Tp>>>{}( + __ref_cast<_Tp>(__t)); + } // Defines members and ctors. template<typename... _Types> @@ -394,8 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_ctor<__storage<_Types>&, - const __storage<_Types>&>... }; + { &__erased_ctor<_Types&, const _Types&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); this->_M_index = __rhs._M_index; } @@ -407,7 +407,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_ctor<__storage<_Types>&, __storage<_Types>&&>... }; + { &__erased_ctor<_Types&, _Types&&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); this->_M_index = __rhs._M_index; } @@ -427,8 +427,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_assign<__storage<_Types>&, - const __storage<_Types>&>... }; + { &__erased_assign<_Types&, const _Types&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); } } @@ -474,8 +473,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_assign<__storage<_Types>&, - __storage<_Types>&&>... }; + { &__erased_assign<_Types&, _Types&&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); } } @@ -560,20 +558,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void* __get_storage(_Variant&& __v) { return __v._M_storage(); } - // A helper used to create variadic number of _To types. - template<typename _From, typename _To> - using _To_type = _To; - - // Call the actual visitor. - // _Args are qualified storage types. - template<typename _Visitor, typename... _Args> - decltype(auto) - __visit_invoke(_Visitor&& __visitor, _To_type<_Args, void*>... __ptrs) - { - return std::forward<_Visitor>(__visitor)( - __get_alternative<_Args>(__ptrs)...); - } - // Used for storing multi-dimensional vtable. template<typename _Tp, size_t... _Dimensions> struct _Multi_array @@ -597,107 +581,112 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; // Creates a multi-dimensional vtable recursively. - // _Variant_tuple is initially the input from visit(), and gets gradually - // consumed. - // _Arg_tuple is enumerated alternative sequence, represented by a - // qualified storage. // // For example, // visit([](auto, auto){}, - // variant<int, char>(), - // variant<float, double, long double>()) + // variant<int, char>(), // typedef'ed as V1 + // variant<float, double, long double>()) // typedef'ed as V2 // will trigger instantiations of: - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 2, 3>, - // tuple<variant<int, char>, - // variant<float, double, long double>>, - // tuple<>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 3>, - // tuple<variant<float, double, long double>>, - // tuple<int>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<int, float>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<int, double>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<int, long double>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 3>, - // tuple<variant<float, double, long double>>, - // tuple<char>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<char, float>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<char, double>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<char, long double>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 2, 3>, + // tuple<V1&&, V2&&>, std::index_sequence<>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>, + // tuple<V1&&, V2&&>, std::index_sequence<0>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<0, 0>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<0, 1>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<0, 2>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>, + // tuple<V1&&, V2&&>, std::index_sequence<1>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<1, 0>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<1, 1>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<1, 2>> // The returned multi-dimensional vtable can be fast accessed by the visitor // using index calculation. - template<typename _Array_type, typename _Variant_tuple, typename _Arg_tuple> + template<typename _Array_type, typename _Variant_tuple, typename _Index_seq> struct __gen_vtable_impl; - template<typename _Array_type, typename _First, typename... _Rest, - typename... _Args> - struct __gen_vtable_impl<_Array_type, tuple<_First, _Rest...>, - tuple<_Args...>> + template<typename _Result_type, typename _Visitor, size_t... __unused, + typename... _Variants, size_t... __indices> + struct __gen_vtable_impl< + _Multi_array<_Result_type (*)(_Visitor, _Variants...), __unused...>, + tuple<_Variants...>, std::index_sequence<__indices...>> { + using _Next = + remove_reference_t<typename _Nth_type<sizeof...(__indices), + _Variants...>::type>; + using _Array_type = + _Multi_array<_Result_type (*)(_Visitor, _Variants...), __unused...>; + static constexpr _Array_type _S_apply() { _Array_type __vtable{}; _S_apply_all_alts( - __vtable, make_index_sequence<variant_size_v<decay_t<_First>>>()); + __vtable, make_index_sequence<variant_size_v<_Next>>()); return __vtable; } - template<size_t... __indices> + template<size_t... __var_indices> static constexpr void - _S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>) - { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); } + _S_apply_all_alts(_Array_type& __vtable, + std::index_sequence<__var_indices...>) + { + (_S_apply_single_alt<__var_indices>( + __vtable._M_arr[__var_indices]), ...); + } template<size_t __index, typename T> static constexpr void _S_apply_single_alt(T& __element) { - using _Alternative = variant_alternative_t<__index, decay_t<_First>>; - using _Qualified_storage = __reserved_type_map< - _First, __storage<_Alternative>>; + using _Alternative = variant_alternative_t<__index, _Next>; __element = __gen_vtable_impl< - decay_t<decltype(__element)>, tuple<_Rest...>, - tuple<_Args..., _Qualified_storage>>::_S_apply(); + remove_reference_t< + decltype(__element)>, tuple<_Variants...>, + std::index_sequence<__indices..., __index>>::_S_apply(); } }; - template<typename _Result_type, typename _Visitor, typename... _Args> + template<typename _Result_type, typename _Visitor, typename... _Variants, + size_t... __indices> struct __gen_vtable_impl< - _Multi_array<_Result_type (*)(_Visitor, _To_type<_Args, void*>...)>, - tuple<>, tuple<_Args...>> + _Multi_array<_Result_type (*)(_Visitor, _Variants...)>, + tuple<_Variants...>, std::index_sequence<__indices...>> { using _Array_type = - _Multi_array<_Result_type (*)(_Visitor&&, _To_type<_Args, void*>...)>; + _Multi_array<_Result_type (*)(_Visitor&&, _Variants...)>; + + decltype(auto) + static constexpr __visit_invoke(_Visitor&& __visitor, _Variants... __vars) + { + return __invoke(std::forward<_Visitor>(__visitor), + std::get<__indices>( + std::forward<_Variants>(__vars))...); + } static constexpr auto _S_apply() - { return _Array_type{&__visit_invoke<_Visitor, _Args...>}; } + { return _Array_type{&__visit_invoke}; } }; template<typename _Result_type, typename _Visitor, typename... _Variants> struct __gen_vtable { - using _Func_ptr = - _Result_type (*)(_Visitor&&, _To_type<_Variants, void*>...); + using _Func_ptr = _Result_type (*)(_Visitor&&, _Variants...); using _Array_type = - _Multi_array<_Func_ptr, variant_size_v<decay_t<_Variants>>...>; + _Multi_array<_Func_ptr, + variant_size_v<remove_reference_t<_Variants>>...>; static constexpr _Array_type _S_apply() { - return __gen_vtable_impl< - _Array_type, tuple<_Variants...>, tuple<>>::_S_apply(); + return __gen_vtable_impl<_Array_type, tuple<_Variants...>, + std::index_sequence<>>::_S_apply(); } }; @@ -727,22 +716,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __v.index() == __detail::__variant::__index_of_v<_Tp, _Types...>; } - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>>& - get(variant<_Types...>&); - - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>>&& - get(variant<_Types...>&&); - - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>> const& - get(const variant<_Types...>&); - - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>> const&& - get(const variant<_Types...>&&); - template<typename _Tp, typename... _Types> constexpr inline _Tp& get(variant<_Types...>& __v) { @@ -867,7 +840,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return !(__lhs < __rhs); } template<typename _Visitor, typename... _Variants> - decltype(auto) visit(_Visitor&&, _Variants&&...); + constexpr decltype(auto) visit(_Visitor&&, _Variants&&...); struct monostate { }; @@ -967,9 +940,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using __accepted_type = __to_type<__accepted_index<_Tp>>; template<typename _Tp> - using __storage = __detail::__variant::__storage<_Tp>; - - template<typename _Tp> static constexpr size_t __index_of = __detail::__variant::__index_of_v<_Tp, _Types...>; @@ -1129,8 +1099,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (this->_M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__detail::__variant::__erased_swap< - __storage<_Types>&, __storage<_Types>&>... }; + { &__detail::__variant::__erased_swap<_Types&, _Types&>... }; _S_vtable[__rhs._M_index](this->_M_storage(), __rhs._M_storage()); } @@ -1270,17 +1239,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Visitor, typename... _Variants> - decltype(auto) + constexpr decltype(auto) visit(_Visitor&& __visitor, _Variants&&... __variants) { + if ((__variants.valueless_by_exception() || ...)) + __throw_bad_variant_access("Unexpected index"); + using _Result_type = decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...)); - static constexpr auto _S_vtable = + constexpr auto _S_vtable = __detail::__variant::__gen_vtable< _Result_type, _Visitor&&, _Variants&&...>::_S_apply(); auto __func_ptr = _S_vtable._M_access(__variants.index()...); return (*__func_ptr)(std::forward<_Visitor>(__visitor), - __detail::__variant::__get_storage(__variants)...); + std::forward<_Variants>(__variants)...); } template<typename... _Types> @@ -1300,7 +1272,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { namespace __edv = __detail::__variant; static constexpr size_t (*_S_vtable[])(void*) = - { &__edv::__erased_hash<const __edv::__storage<_Types>&>... }; + { &__edv::__erased_hash<const _Types&>... }; return hash<size_t>{}(__t.index()) + _S_vtable[__t.index()](__edv::__get_storage(__t)); } diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index b973143..8f77ffb 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -260,6 +260,22 @@ void test_visit() }; visit(Visitor(), variant<int, char>(), variant<float, double>()); } + { + struct Visitor + { + constexpr bool operator()(const int&) { return true; } + constexpr bool operator()(const nonliteral&) { return false; } + }; + static_assert(visit(Visitor(), variant<int, nonliteral>(0)), ""); + } + { + struct Visitor + { + constexpr bool operator()(const int&) { return true; } + constexpr bool operator()(const nonliteral&) { return false; } + }; + static_assert(visit(Visitor(), variant<int, nonliteral>(0)), ""); + } } void test_constexpr() ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-11-27 5:38 [Patches] Add variant constexpr support for visit, comparisons and get Tim Shen @ 2016-11-30 16:27 ` Jonathan Wakely 2016-12-01 3:29 ` Tim Shen 2016-12-03 3:15 ` Tim Shen 0 siblings, 2 replies; 10+ messages in thread From: Jonathan Wakely @ 2016-11-30 16:27 UTC (permalink / raw) To: Tim Shen; +Cc: libstdc++, gcc-patches On 26/11/16 21:38 -0800, Tim Shen wrote: >This 4-patch series contains the following in order: > >a.diff: Remove uses-allocator ctors. They are going away, and removing >it reduces the maintenance burden from now on. Yay! less code. >b.diff: Add constexpr support for get<> and comparisons. This patch >also involves small refactoring of _Variant_storage. > >c.diff: Fix some libc++ test failures. > >d.diff: Add constexpr support for visit. This patch also removes >__storage, __get_alternative, and __reserved_type_map, since we don't >need to support reference/void types for now. > >The underlying design doesn't change - we still use the vtable >approach to achieve O(1) runtime cost even under -O0. Great stuff. > * include/std/variant: Implement constexpr comparison and get<>. > * testsuite/20_util/variant/compile.cc: Tests. > >diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant >index 2d9303a..a913074 100644 >--- a/libstdc++-v3/include/std/variant >+++ b/libstdc++-v3/include/std/variant >@@ -154,31 +154,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > template<typename _Alternative> > using __storage = _Alternative; > >- template<typename _Type, bool __is_literal = std::is_literal_type_v<_Type>> >+ // _Uninitialized nullify the destructor calls. This wording confused me slightly. How about: "_Uninitialized makes destructors trivial" >+ // This is necessary, since we define _Variadic_union as a recursive union, >+ // and we don't want to inspect the union members one by one in its dtor, >+ // it's slow. Please change "it's slow" to "that's slow". >+ template<typename _Type, bool = std::is_literal_type_v<_Type>> > struct _Uninitialized; I'm still unsure that is_literal_type is the right trait here. If it's definitely right then we should probably *not* deprecate it in C++17! > template<typename _Type> > struct _Uninitialized<_Type, false> > { >- constexpr _Uninitialized() = default; >- > template<typename... _Args> > constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) > { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); } > >+ const _Type& _M_get() const & >+ { >+ return *static_cast<const _Type*>( >+ static_cast<const void*>(&_M_storage)); >+ } >+ >+ _Type& _M_get() & >+ { return *static_cast<_Type*>(static_cast<void*>(&_M_storage)); } >+ >+ const _Type&& _M_get() const && >+ { >+ return std::move(*static_cast<const _Type*>( >+ static_cast<const void*>(&_M_storage))); >+ } >+ >+ _Type&& _M_get() && >+ { >+ return std::move(*static_cast<_Type*>(static_cast<void*>(&_M_storage))); >+ } >+ > typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type > _M_storage; I think this could use __aligned_membuf, which would reduce the alignment requirements for some types (e.g. long long on x86-32). That would also mean you get the _M_ptr() member so don't need all the casts. >+ ~_Variant_storage() >+ { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } You can use index_sequence_for<_Types...> here. >@@ -598,9 +645,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > _S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>) > { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); } > >- template<size_t __index> >+ template<size_t __index, typename T> This needs to be _Tp not T >+ return __lhs._M_equal_to(__rhs, >+ std::make_index_sequence<sizeof...(_Types)>{}); Another one that could use index_sequence_for<_Types...> >+ return __lhs._M_less_than(__rhs, >+ std::make_index_sequence<sizeof...(_Types)>{}); Same again. > * 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. > >diff --git a/libstdc++-v3/include/bits/enable_special_members.h b/libstdc++-v3/include/bits/enable_special_members.h >index 07c6c99..4f4477b 100644 >--- a/libstdc++-v3/include/bits/enable_special_members.h >+++ b/libstdc++-v3/include/bits/enable_special_members.h >@@ -118,7 +118,8 @@ template<typename _Tag> > 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) { } > }; > >+ void _M_reset() >+ { >+ _M_reset_impl(std::make_index_sequence<sizeof...(_Types)>{}); >+ _M_index = variant_npos; >+ } >+ > ~_Variant_storage() >- { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } >+ { _M_reset(); } These can also use index_sequence_for<_Types...> >@@ -1253,14 +1285,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<typename... _Types> > struct hash<variant<_Types...>> >- : private __poison_hash<remove_const_t<_Types>>... >+ : private __detail::__variant::_Variant_hash_base< >+ variant<_Types...>, std::make_index_sequence<sizeof...(_Types)>> And again. > { > using result_type = size_t; > using argument_type = variant<_Types...>; > > size_t > operator()(const variant<_Types...>& __t) const >- noexcept((... && noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())))) >+ noexcept((noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())) >+ && ...)) This could be __and_<is_nothrow_callable<hash<decay_t<_Types>>(_Types)>...> but I'm not sure it would be an improvement. The is_callable check is expensive, but maybe we need it anyway to correctly disable this function if the hash specialization should be posisoned? >@@ -1270,17 +1239,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > > template<typename _Visitor, typename... _Variants> >- decltype(auto) >+ constexpr decltype(auto) > visit(_Visitor&& __visitor, _Variants&&... __variants) > { >+ if ((__variants.valueless_by_exception() || ...)) >+ __throw_bad_variant_access("Unexpected index"); >+ > using _Result_type = > decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...)); >- static constexpr auto _S_vtable = >+ constexpr auto _S_vtable = If this isn't static now it could be called simply __vtable, the _S_ prefix is misleading. How many of these _S_vtable variables actually need to be static? If they're all trivial types and constexpr then it probably doesn't matter either way, there shouldn't be any difference. > __detail::__variant::__gen_vtable< > _Result_type, _Visitor&&, _Variants&&...>::_S_apply(); > auto __func_ptr = _S_vtable._M_access(__variants.index()...); > return (*__func_ptr)(std::forward<_Visitor>(__visitor), >- __detail::__variant::__get_storage(__variants)...); >+ std::forward<_Variants>(__variants)...); > } ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-11-30 16:27 ` Jonathan Wakely @ 2016-12-01 3:29 ` Tim Shen 2016-12-01 11:16 ` Jonathan Wakely 2016-12-03 3:15 ` Tim Shen 1 sibling, 1 reply; 10+ messages in thread From: Tim Shen @ 2016-12-01 3:29 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, gcc-patches On Wed, Nov 30, 2016 at 8:27 AM, Jonathan Wakely wrote: > On 26/11/16 21:38 -0800, Tim Shen wrote: >> + template<typename _Type, bool = std::is_literal_type_v<_Type>> >> struct _Uninitialized; > > > I'm still unsure that is_literal_type is the right trait here. If it's > definitely right then we should probably *not* deprecate it in C++17! No it's not right. We need this only because [basic.types]p10.5.3 (in n4606): if it (a literal type) is a union, at least one of its non-static data members is of non-volatile literal type, ... is not implemented. In the current GCC implementation, however, all non-static data members need to be literal types, in order to create a literal union. With the current GCC implementation, to keep our goal, which is to make _Variadic_union literal type, we need to ensure that _Uninitialized<T> is literal type, by specializing on T: 1) If is_literal_type_v<T>, store a T; 2) otherwise, store a raw buffer of T. In the future, when [basic.types]p10.5.3 is implemented, we don't need is_literal_type_v. I'll add a comment here. I didn't check for other compilers. -- Regards, Tim Shen ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-12-01 3:29 ` Tim Shen @ 2016-12-01 11:16 ` Jonathan Wakely 0 siblings, 0 replies; 10+ messages in thread From: Jonathan Wakely @ 2016-12-01 11:16 UTC (permalink / raw) To: Tim Shen; +Cc: libstdc++, gcc-patches On 30/11/16 19:29 -0800, Tim Shen wrote: >On Wed, Nov 30, 2016 at 8:27 AM, Jonathan Wakely wrote: >> On 26/11/16 21:38 -0800, Tim Shen wrote: >>> + template<typename _Type, bool = std::is_literal_type_v<_Type>> >>> struct _Uninitialized; >> >> >> I'm still unsure that is_literal_type is the right trait here. If it's >> definitely right then we should probably *not* deprecate it in C++17! > >No it's not right. We need this only because [basic.types]p10.5.3 (in n4606): > > if it (a literal type) is a union, at least one of its non-static >data members is of non-volatile literal type, ... > >is not implemented. In the current GCC implementation, however, all >non-static data members need to be literal types, in order to create a >literal union. > >With the current GCC implementation, to keep our goal, which is to >make _Variadic_union literal type, we need to ensure that >_Uninitialized<T> is literal type, by specializing on T: >1) If is_literal_type_v<T>, store a T; >2) otherwise, store a raw buffer of T. > >In the future, when [basic.types]p10.5.3 is implemented, we don't need >is_literal_type_v. > >I'll add a comment here. Thanks, that will stop me asking again and again in future ;-) I think we want to get [basic.types] p10 implemented before we declare C++17 support non-experimental, so we don't have to change std::variant layout later. >I didn't check for other compilers. That's fine, the current approach should work for them too. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-11-30 16:27 ` Jonathan Wakely 2016-12-01 3:29 ` Tim Shen @ 2016-12-03 3:15 ` Tim Shen 2016-12-06 10:30 ` Jonathan Wakely 1 sibling, 1 reply; 10+ messages in thread From: Tim Shen @ 2016-12-03 3:15 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 6799 bytes --] On Wed, Nov 30, 2016 at 8:27 AM, Jonathan Wakely wrote: > On 26/11/16 21:38 -0800, Tim Shen wrote: >> >> This 4-patch series contains the following in order: >> >> a.diff: Remove uses-allocator ctors. They are going away, and removing >> it reduces the maintenance burden from now on. > > > Yay! less code. Yay! Also removed the unused #include. > >> - template<typename _Type, bool __is_literal = >> std::is_literal_type_v<_Type>> >> + // _Uninitialized nullify the destructor calls. > > > This wording confused me slightly. How about: > > "_Uninitialized makes destructors trivial" Change this section of comment to the discussed content. > >> + // This is necessary, since we define _Variadic_union as a recursive >> union, >> + // and we don't want to inspect the union members one by one in its >> dtor, >> + // it's slow. > > > Please change "it's slow" to "that's slow". N/A. > >> + template<typename _Type, bool = std::is_literal_type_v<_Type>> >> struct _Uninitialized; > > > I'm still unsure that is_literal_type is the right trait here. If it's > definitely right then we should probably *not* deprecate it in C++17! Already discussed. > >> template<typename _Type> >> struct _Uninitialized<_Type, false> >> { >> - constexpr _Uninitialized() = default; >> - >> template<typename... _Args> >> constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) >> { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); } >> >> + const _Type& _M_get() const & >> + { >> + return *static_cast<const _Type*>( >> + static_cast<const void*>(&_M_storage)); >> + } >> + >> + _Type& _M_get() & >> + { return *static_cast<_Type*>(static_cast<void*>(&_M_storage)); } >> + >> + const _Type&& _M_get() const && >> + { >> + return std::move(*static_cast<const _Type*>( >> + static_cast<const void*>(&_M_storage))); >> + } >> + >> + _Type&& _M_get() && >> + { >> + return >> std::move(*static_cast<_Type*>(static_cast<void*>(&_M_storage))); >> + } >> + >> typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type >> _M_storage; > > > I think this could use __aligned_membuf, which would reduce the > alignment requirements for some types (e.g. long long on x86-32). Done. > > That would also mean you get the _M_ptr() member so don't need all the > casts. > >> + ~_Variant_storage() >> + { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } > > > You can use index_sequence_for<_Types...> here. Done > >> @@ -598,9 +645,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> _S_apply_all_alts(_Array_type& __vtable, >> index_sequence<__indices...>) >> { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), >> ...); } >> >> - template<size_t __index> >> + template<size_t __index, typename T> > > > This needs to be _Tp not T Done. > >> + return __lhs._M_equal_to(__rhs, >> + >> std::make_index_sequence<sizeof...(_Types)>{}); > > > Another one that could use index_sequence_for<_Types...> Done. > >> + return __lhs._M_less_than(__rhs, >> + >> std::make_index_sequence<sizeof...(_Types)>{}); > > > Same again. Same again. ;) > > >> * 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. >> >> diff --git a/libstdc++-v3/include/bits/enable_special_members.h >> b/libstdc++-v3/include/bits/enable_special_members.h >> index 07c6c99..4f4477b 100644 >> --- a/libstdc++-v3/include/bits/enable_special_members.h >> +++ b/libstdc++-v3/include/bits/enable_special_members.h >> @@ -118,7 +118,8 @@ template<typename _Tag> >> 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) { } >> }; >> >> + void _M_reset() >> + { >> + _M_reset_impl(std::make_index_sequence<sizeof...(_Types)>{}); >> + _M_index = variant_npos; >> + } >> + >> ~_Variant_storage() >> - { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } >> + { _M_reset(); } > > > These can also use index_sequence_for<_Types...> Done. > >> @@ -1253,14 +1285,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> >> template<typename... _Types> >> struct hash<variant<_Types...>> >> - : private __poison_hash<remove_const_t<_Types>>... >> + : private __detail::__variant::_Variant_hash_base< >> + variant<_Types...>, std::make_index_sequence<sizeof...(_Types)>> > > > And again. And again. > >> { >> using result_type = size_t; >> using argument_type = variant<_Types...>; >> >> size_t >> operator()(const variant<_Types...>& __t) const >> - noexcept((... && >> noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())))) >> + noexcept((noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())) >> + && ...)) > > > This could be > __and_<is_nothrow_callable<hash<decay_t<_Types>>(_Types)>...> > but I'm not sure it would be an improvement. The is_callable check is > expensive, but maybe we need it anyway to correctly disable this > function if the hash specialization should be posisoned? Done. I just realized that is_nothrow_callable also handles crazy member pointer cases. Used fold expression instead of __and_ for consistency. > > >> @@ -1270,17 +1239,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> } >> >> template<typename _Visitor, typename... _Variants> >> - decltype(auto) >> + constexpr decltype(auto) >> visit(_Visitor&& __visitor, _Variants&&... __variants) >> { >> + if ((__variants.valueless_by_exception() || ...)) >> + __throw_bad_variant_access("Unexpected index"); >> + >> using _Result_type = >> >> decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...)); >> - static constexpr auto _S_vtable = >> + constexpr auto _S_vtable = > > > If this isn't static now it could be called simply __vtable, the _S_ > prefix is misleading. How many of these _S_vtable variables actually > need to be static? If they're all trivial types and constexpr then it > probably doesn't matter either way, there shouldn't be any difference. Ah that's an oversight. Moved the static variable out of visit(). _S_vtable needs to be static, otherwise runtime O(n) assignment will happen, where n is the size of _S_vtable. -- Regards, Tim Shen [-- Attachment #2: a2.diff --] [-- Type: text/x-patch, Size: 10744 bytes --] commit 896de64db984a82f59090c58ee86555b79052346 Author: Tim Shen <timshen@google.com> Date: Thu Nov 24 00:56:08 2016 -0800 2016-11-26 Tim Shen <timshen@google.com> * include/std/variant (__erased_use_alloc_ctor, _Variant_base::_Variant_base, variant::variant): Remove uses-allocator related functions. * testsuite/20_util/variant/compile.cc: Remove related tests. * testsuite/20_util/variant/run.cc: Remove related tests. diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 89ca979..32c0dc3 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -40,7 +40,6 @@ #include <bits/enable_special_members.h> #include <bits/functexcept.h> #include <bits/move.h> -#include <bits/uses_allocator.h> #include <bits/functional_hash.h> namespace std _GLIBCXX_VISIBILITY(default) @@ -202,14 +201,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __erased_ctor(void* __lhs, void* __rhs) { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); } - template<typename _Alloc, typename _Lhs, typename _Rhs> - constexpr void - __erased_use_alloc_ctor(const _Alloc& __a, void* __lhs, void* __rhs) - { - __uses_allocator_construct(__a, static_cast<decay_t<_Lhs>*>(__lhs), - __get_alternative<_Rhs>(__rhs)); - } - // TODO: Find a potential chance to reuse this accross the project. template<typename _Tp> constexpr void @@ -353,47 +344,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _Storage(__i, std::forward<_Args>(__args)...), _M_index(_Np) { } - template<typename _Alloc> - _Variant_base(const _Alloc& __a, const _Variant_base& __rhs) - : _Storage(), _M_index(__rhs._M_index) - { - if (__rhs._M_valid()) - { - static constexpr void - (*_S_vtable[])(const _Alloc&, void*, void*) = - { &__erased_use_alloc_ctor<_Alloc, __storage<_Types>&, - const __storage<_Types>&>... }; - _S_vtable[__rhs._M_index](__a, _M_storage(), __rhs._M_storage()); - } - } - - template<typename _Alloc> - _Variant_base(const _Alloc& __a, _Variant_base&& __rhs) - : _Storage(), _M_index(__rhs._M_index) - { - if (__rhs._M_valid()) - { - static constexpr void - (*_S_vtable[])(const _Alloc&, void*, void*) = - { &__erased_use_alloc_ctor<_Alloc, __storage<_Types>&, - __storage<_Types>&&>... }; - _S_vtable[__rhs._M_index](__a, _M_storage(), __rhs._M_storage()); - } - } - - template<typename _Alloc, size_t _Np, typename... _Args> - constexpr explicit - _Variant_base(const _Alloc& __a, in_place_index_t<_Np>, - _Args&&... __args) - : _Storage(), _M_index(_Np) - { - using _Storage = - __storage<variant_alternative_t<_Np, variant<_Types...>>>; - __uses_allocator_construct(__a, static_cast<_Storage*>(_M_storage()), - std::forward<_Args>(__args)...); - __glibcxx_assert(_M_index == _Np); - } - _Variant_base& operator=(const _Variant_base& __rhs) { @@ -1033,84 +983,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Default_ctor_enabler(_Enable_default_constructor_tag{}) { __glibcxx_assert(index() == _Np); } - template<typename _Alloc, - typename = enable_if_t< - __is_uses_allocator_constructible_v<__to_type<0>, _Alloc>>> - variant(allocator_arg_t, const _Alloc& __a) - : variant(allocator_arg, __a, in_place_index<0>) - { } - - template<typename _Alloc, - typename = enable_if_t<__and_<__is_uses_allocator_constructible< - _Types, _Alloc, - add_lvalue_reference_t<add_const_t<_Types>>>...>::value>> - variant(allocator_arg_t, const _Alloc& __a, const variant& __rhs) - : _Base(__a, __rhs), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { } - - template<typename _Alloc, - typename = enable_if_t<__and_< - __is_uses_allocator_constructible< - _Types, _Alloc, add_rvalue_reference_t<_Types>>...>::value>> - variant(allocator_arg_t, const _Alloc& __a, variant&& __rhs) - : _Base(__a, std::move(__rhs)), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { } - - template<typename _Alloc, typename _Tp, - typename = enable_if_t< - __exactly_once<__accepted_type<_Tp&&>> - && __is_uses_allocator_constructible_v< - __accepted_type<_Tp&&>, _Alloc, _Tp&&> - && !is_same_v<decay_t<_Tp>, variant>, variant&>> - variant(allocator_arg_t, const _Alloc& __a, _Tp&& __t) - : variant(allocator_arg, __a, in_place_index<__accepted_index<_Tp&&>>, - std::forward<_Tp>(__t)) - { __glibcxx_assert(holds_alternative<__accepted_type<_Tp&&>>(*this)); } - - template<typename _Alloc, typename _Tp, typename... _Args, - typename = enable_if_t< - __exactly_once<_Tp> - && __is_uses_allocator_constructible_v< - _Tp, _Alloc, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_type_t<_Tp>, - _Args&&... __args) - : variant(allocator_arg, __a, in_place_index<__index_of<_Tp>>, - std::forward<_Args>(__args)...) - { __glibcxx_assert(holds_alternative<_Tp>(*this)); } - - template<typename _Alloc, typename _Tp, typename _Up, typename... _Args, - typename = enable_if_t< - __exactly_once<_Tp> - && __is_uses_allocator_constructible_v< - _Tp, _Alloc, initializer_list<_Up>&, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_type_t<_Tp>, - initializer_list<_Up> __il, _Args&&... __args) - : variant(allocator_arg, __a, in_place_index<__index_of<_Tp>>, __il, - std::forward<_Args>(__args)...) - { __glibcxx_assert(holds_alternative<_Tp>(*this)); } - - template<typename _Alloc, size_t _Np, typename... _Args, - typename = enable_if_t< - __is_uses_allocator_constructible_v< - __to_type<_Np>, _Alloc, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_index_t<_Np>, - _Args&&... __args) - : _Base(__a, in_place_index<_Np>, std::forward<_Args>(__args)...), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { __glibcxx_assert(index() == _Np); } - - template<typename _Alloc, size_t _Np, typename _Up, typename... _Args, - typename = enable_if_t< - __is_uses_allocator_constructible_v< - __to_type<_Np>, _Alloc, initializer_list<_Up>&, _Args&&...>>> - variant(allocator_arg_t, const _Alloc& __a, in_place_index_t<_Np>, - initializer_list<_Up> __il, _Args&&... __args) - : _Base(__a, in_place_index<_Np>, __il, std::forward<_Args>(__args)...), - _Default_ctor_enabler(_Enable_default_constructor_tag{}) - { __glibcxx_assert(index() == _Np); } - ~variant() = default; variant& operator=(const variant&) = default; @@ -1300,10 +1172,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __detail::__variant::__get_storage(__variants)...); } - template<typename... _Types, typename _Alloc> - struct uses_allocator<variant<_Types...>, _Alloc> - : true_type { }; - template<typename... _Types> struct hash<variant<_Types...>> : private __poison_hash<remove_const_t<_Types>>... diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index 8250a95..a67b651 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -117,31 +117,6 @@ void in_place_type_ctor() static_assert(!is_constructible_v<variant<string, string>, in_place_type_t<string>, const char*>, ""); } -void uses_alloc_ctors() -{ - std::allocator<char> alloc; - variant<int> a(allocator_arg, alloc); - static_assert(!is_constructible_v<variant<AllDeleted>, allocator_arg_t, std::allocator<char>>, ""); - { - variant<string, int> b(allocator_arg, alloc, "a"); - static_assert(!is_constructible_v<variant<string, string>, allocator_arg_t, std::allocator<char>, const char*>, ""); - } - { - variant<string, int> b(allocator_arg, alloc, in_place_index<0>, "a"); - variant<string, string> c(allocator_arg, alloc, in_place_index<1>, "a"); - } - { - variant<string, int> b(allocator_arg, alloc, in_place_index<0>, {'a'}); - variant<string, string> c(allocator_arg, alloc, in_place_index<1>, {'a'}); - } - { - variant<int, string, int> b(allocator_arg, alloc, in_place_type<string>, "a"); - } - { - variant<int, string, int> b(allocator_arg, alloc, in_place_type<string>, {'a'}); - } -} - void dtor() { static_assert(is_destructible_v<variant<int, string>>, ""); @@ -324,9 +299,7 @@ namespace adl_trap void test_adl() { using adl_trap::X; - using std::allocator_arg; X x; - std::allocator<int> a; std::initializer_list<int> il; adl_trap::Visitor vis; @@ -339,11 +312,6 @@ void test_adl() variant<X> v2{in_place_type<X>, x}; variant<X> v3{in_place_index<0>, il, x}; variant<X> v4{in_place_type<X>, il, x}; - variant<X> v5{allocator_arg, a, in_place_index<0>, x}; - variant<X> v6{allocator_arg, a, in_place_type<X>, x}; - variant<X> v7{allocator_arg, a, in_place_index<0>, il, x}; - variant<X> v8{allocator_arg, a, in_place_type<X>, il, x}; - variant<X> v9{allocator_arg, a, in_place_type<X>, 1}; } void test_variant_alternative() { diff --git a/libstdc++-v3/testsuite/20_util/variant/run.cc b/libstdc++-v3/testsuite/20_util/variant/run.cc index 71e0176..fb5d7c4 100644 --- a/libstdc++-v3/testsuite/20_util/variant/run.cc +++ b/libstdc++-v3/testsuite/20_util/variant/run.cc @@ -160,48 +160,6 @@ void in_place_type_ctor() } } -struct UsesAllocatable -{ - template<typename Alloc> - UsesAllocatable(std::allocator_arg_t, const Alloc& a) - : d(0), a(static_cast<const void*>(&a)) { } - - template<typename Alloc> - UsesAllocatable(std::allocator_arg_t, const Alloc& a, const UsesAllocatable&) - : d(1), a(static_cast<const void*>(&a)) { } - - template<typename Alloc> - UsesAllocatable(std::allocator_arg_t, const Alloc& a, UsesAllocatable&&) - : d(2), a(static_cast<const void*>(&a)) { } - - int d; - const void* a; -}; - -namespace std -{ - template<> - struct uses_allocator<UsesAllocatable, std::allocator<char>> : true_type { }; -} - -void uses_allocator_ctor() -{ - std::allocator<char> a; - variant<UsesAllocatable> v(std::allocator_arg, a); - VERIFY(get<0>(v).d == 0); - VERIFY(get<0>(v).a == &a); - { - variant<UsesAllocatable> u(std::allocator_arg, a, v); - VERIFY(get<0>(u).d == 1); - VERIFY(get<0>(u).a == &a); - } - { - variant<UsesAllocatable> u(std::allocator_arg, a, std::move(v)); - VERIFY(get<0>(u).d == 2); - VERIFY(get<0>(u).a == &a); - } -} - void emplace() { variant<int, string> v; @@ -450,7 +408,6 @@ int main() arbitrary_ctor(); in_place_index_ctor(); in_place_type_ctor(); - uses_allocator_ctor(); copy_assign(); move_assign(); arbitrary_assign(); [-- Attachment #3: b2.diff --] [-- Type: text/x-patch, Size: 28893 bytes --] commit 7af7721bc130f768f5659c8faf50e3d76d63a7d1 Author: Tim Shen <timshen@google.com> Date: Thu Nov 24 12:00:48 2016 -0800 2016-11-26 Tim Shen <timshen@google.com> * include/std/variant: Implement constexpr comparison and get<>. * testsuite/20_util/variant/compile.cc: Tests. diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 32c0dc3..a961a05 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -41,6 +41,7 @@ #include <bits/functexcept.h> #include <bits/move.h> #include <bits/functional_hash.h> +#include <ext/aligned_buffer.h> namespace std _GLIBCXX_VISIBILITY(default) { @@ -153,33 +154,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Alternative> using __storage = _Alternative; - template<typename _Type, bool __is_literal = std::is_literal_type_v<_Type>> + // _Uninitialized<T> is guaranteed to be a literal type, even if T is not. + // We have to do this, because [basic.types]p10.5.3 (n4606) is not implemented + // yet. When it's implemented, _Uninitialized<T> can be changed to the alias + // to T, therefore equivalent to being removed entirely. + // + // Another reason we may not want to remove _Uninitialzied<T> may be that, we + // want _Uninitialized<T> to be trivially destructible, no matter whether T + // is; but we will see. + template<typename _Type, bool = std::is_literal_type_v<_Type>> struct _Uninitialized; template<typename _Type> struct _Uninitialized<_Type, true> { - constexpr _Uninitialized() = default; - template<typename... _Args> constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) : _M_storage(std::forward<_Args>(__args)...) { } + constexpr const _Type& _M_get() const & + { return _M_storage; } + + constexpr _Type& _M_get() & + { return _M_storage; } + + constexpr const _Type&& _M_get() const && + { return std::move(_M_storage); } + + constexpr _Type&& _M_get() && + { return std::move(_M_storage); } + _Type _M_storage; }; template<typename _Type> struct _Uninitialized<_Type, false> { - constexpr _Uninitialized() = default; - template<typename... _Args> constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); } - typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type - _M_storage; + const _Type& _M_get() const & + { return *_M_storage._M_ptr(); } + + _Type& _M_get() & + { return *_M_storage._M_ptr(); } + + const _Type&& _M_get() const && + { return std::move(*_M_storage._M_ptr()); } + + _Type&& _M_get() && + { return std::move(*_M_storage._M_ptr()); } + + __gnu_cxx::__aligned_membuf<_Type> _M_storage; }; // Given a qualified storage type, return the desired reference. @@ -194,6 +222,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION *static_cast<_Storage*>(__ptr)); } + template<typename _Union> + constexpr decltype(auto) __get(in_place_index_t<0>, _Union&& __u) + { return std::forward<_Union>(__u)._M_first._M_get(); } + + template<size_t _Np, typename _Union> + constexpr decltype(auto) __get(in_place_index_t<_Np>, _Union&& __u) + { return __get(in_place_index<_Np-1>, std::forward<_Union>(__u)._M_rest); } + + // Returns the typed storage for __v. + template<size_t _Np, typename _Variant> + constexpr decltype(auto) __get(_Variant&& __v) + { + return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u); + } + // Various functions as "vtable" entries, where those vtables are used by // polymorphic operations. template<typename _Lhs, typename _Rhs> @@ -201,13 +244,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __erased_ctor(void* __lhs, void* __rhs) { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); } - // TODO: Find a potential chance to reuse this accross the project. - template<typename _Tp> + template<typename _Variant, size_t _Np> constexpr void - __erased_dtor(void* __ptr) + __erased_dtor(_Variant&& __v) { - using _Storage = decay_t<_Tp>; - static_cast<_Storage*>(__ptr)->~_Storage(); + auto&& __element = __get<_Np>(std::forward<_Variant>(__v)); + using _Type = std::remove_reference_t<decltype(__element)>; + __element.~_Type(); } template<typename _Lhs, typename _Rhs> @@ -223,90 +266,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs)); } - template<typename _Lhs, typename _Rhs> + template<typename _Variant, size_t _Np> constexpr bool - __erased_equal_to(void* __lhs, void* __rhs) - { return __get_alternative<_Lhs>(__lhs) == __get_alternative<_Rhs>(__rhs); } + __erased_equal_to(_Variant&& __lhs, _Variant&& __rhs) + { + return __get<_Np>(std::forward<_Variant>(__lhs)) + == __get<_Np>(std::forward<_Variant>(__rhs)); + } - template<typename _Lhs, typename _Rhs> + template<typename _Variant, size_t _Np> constexpr bool - __erased_less_than(void* __lhs, void* __rhs) - { return __get_alternative<_Lhs>(__lhs) < __get_alternative<_Rhs>(__rhs); } + __erased_less_than(const _Variant& __lhs, const _Variant& __rhs) + { + return __get<_Np>(std::forward<_Variant>(__lhs)) + < __get<_Np>(std::forward<_Variant>(__rhs)); + } template<typename _Tp> constexpr size_t __erased_hash(void* __t) { return std::hash<decay_t<_Tp>>{}(__get_alternative<_Tp>(__t)); } + // Defines members and ctors. template<typename... _Types> - struct _Variant_base; + union _Variadic_union { }; - template<typename... _Types> - struct _Variant_storage - { constexpr _Variant_storage() = default; }; - - // Use recursive unions to implement a trivially destructible variant. template<typename _First, typename... _Rest> - struct _Variant_storage<_First, _Rest...> + union _Variadic_union<_First, _Rest...> { - constexpr _Variant_storage() = default; + constexpr _Variadic_union() : _M_rest() { } + + template<typename... _Args> + constexpr _Variadic_union(in_place_index_t<0>, _Args&&... __args) + : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) + { } + + template<size_t _Np, typename... _Args> + constexpr _Variadic_union(in_place_index_t<_Np>, _Args&&... __args) + : _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...) + { } + + _Uninitialized<_First> _M_first; + _Variadic_union<_Rest...> _M_rest; + }; + + // Defines index and the dtor, possibly trivial. + template<bool __trivially_destructible, typename... _Types> + struct _Variant_storage; + + template<typename... _Types> + struct _Variant_storage<false, _Types...> + { + template<size_t... __indices> + static constexpr void (*_S_vtable[])(const _Variant_storage&) = + { &__erased_dtor<const _Variant_storage&, __indices>... }; + + constexpr _Variant_storage() : _M_index(variant_npos) { } template<size_t _Np, typename... _Args> constexpr _Variant_storage(in_place_index_t<_Np>, _Args&&... __args) - : _M_union(in_place_index<_Np>, std::forward<_Args>(__args)...) + : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...), + _M_index(_Np) { } - ~_Variant_storage() = default; + template<size_t... __indices> + constexpr void _M_destroy_impl(std::index_sequence<__indices...>) + { + if (_M_index != variant_npos) + _S_vtable<__indices...>[_M_index](*this); + } - constexpr void* - _M_storage() const - { - return const_cast<void*>( - static_cast<const void*>(std::addressof(_M_union._M_first._M_storage))); - } + ~_Variant_storage() + { _M_destroy_impl(std::index_sequence_for<_Types...>{}); } - union _Union - { - constexpr _Union() {}; - - template<typename... _Args> - constexpr _Union(in_place_index_t<0>, _Args&&... __args) - : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) - { } - - template<size_t _Np, typename... _Args, - typename = enable_if_t<0 < _Np && _Np < sizeof...(_Rest) + 1>> - constexpr _Union(in_place_index_t<_Np>, _Args&&... __args) - : _M_rest(in_place_index<_Np - 1>, std::forward<_Args>(__args)...) - { } - - _Uninitialized<__storage<_First>> _M_first; - _Variant_storage<_Rest...> _M_rest; - } _M_union; + _Variadic_union<_Types...> _M_u; + size_t _M_index; }; - template<typename _Derived, bool __is_trivially_destructible> - struct _Dtor_mixin + template<typename... _Types> + struct _Variant_storage<true, _Types...> { - ~_Dtor_mixin() - { static_cast<_Derived*>(this)->_M_destroy(); } - }; + constexpr _Variant_storage() : _M_index(variant_npos) { } - template<typename _Derived> - struct _Dtor_mixin<_Derived, true> - { - ~_Dtor_mixin() = default; + template<size_t _Np, typename... _Args> + constexpr _Variant_storage(in_place_index_t<_Np>, _Args&&... __args) + : _M_u(in_place_index<_Np>, std::forward<_Args>(__args)...), + _M_index(_Np) + { } + + _Variadic_union<_Types...> _M_u; + size_t _M_index; }; // Helps SFINAE on special member functions. Otherwise it can live in variant // class. template<typename... _Types> struct _Variant_base : - _Variant_storage<_Types...>, - _Dtor_mixin<_Variant_base<_Types...>, - __and_<std::is_trivially_destructible<_Types>...>::value> + _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...), + _Types...> { - using _Storage = _Variant_storage<_Types...>; + using _Storage = + _Variant_storage<(std::is_trivially_destructible_v<_Types> && ...), + _Types...>; constexpr _Variant_base() @@ -315,7 +376,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _Variant_base(in_place_index<0>) { } _Variant_base(const _Variant_base& __rhs) - : _Storage(), _M_index(__rhs._M_index) { if (__rhs._M_valid()) { @@ -323,31 +383,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { &__erased_ctor<__storage<_Types>&, const __storage<_Types>&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); + this->_M_index = __rhs._M_index; } } _Variant_base(_Variant_base&& __rhs) noexcept(__and_<is_nothrow_move_constructible<_Types>...>::value) - : _Storage(), _M_index(__rhs._M_index) { if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = { &__erased_ctor<__storage<_Types>&, __storage<_Types>&&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); + this->_M_index = __rhs._M_index; } } template<size_t _Np, typename... _Args> constexpr explicit _Variant_base(in_place_index_t<_Np> __i, _Args&&... __args) - : _Storage(__i, std::forward<_Args>(__args)...), _M_index(_Np) + : _Storage(__i, std::forward<_Args>(__args)...) { } _Variant_base& operator=(const _Variant_base& __rhs) { - if (_M_index == __rhs._M_index) + if (this->_M_index == __rhs._M_index) { if (__rhs._M_valid()) { @@ -367,11 +428,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } __catch (...) { - _M_index = variant_npos; + this->_M_index = variant_npos; __throw_exception_again; } } - __glibcxx_assert(_M_index == __rhs._M_index); + __glibcxx_assert(this->_M_index == __rhs._M_index); return *this; } @@ -380,7 +441,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(__and_<is_nothrow_move_constructible<_Types>..., is_nothrow_move_assignable<_Types>...>::value) { - if (_M_index == __rhs._M_index) + if (this->_M_index == __rhs._M_index) { if (__rhs._M_valid()) { @@ -399,32 +460,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } __catch (...) { - _M_index = variant_npos; + this->_M_index = variant_npos; __throw_exception_again; } } return *this; } - void _M_destroy() + void* + _M_storage() const { - if (_M_valid()) - { - static constexpr void (*_S_vtable[])(void*) = - { &__erased_dtor<__storage<_Types>&>... }; - _S_vtable[this->_M_index](_M_storage()); - } + return const_cast<void*>(static_cast<const void*>( + std::addressof(_Storage::_M_u))); } - constexpr void* - _M_storage() const - { return _Storage::_M_storage(); } - constexpr bool _M_valid() const noexcept - { return _M_index != variant_npos; } - - size_t _M_index; + { return this->_M_index != variant_npos; } }; // For how many times does _Tp appear in _Tuple? @@ -489,15 +541,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void* __get_storage(_Variant&& __v) { return __v._M_storage(); } - // Returns the reference to the desired alternative. - // It is as unsafe as a reinterpret_cast. - template<typename _Tp, typename _Variant> - decltype(auto) __access(_Variant&& __v) - { - return __get_alternative<__reserved_type_map<_Variant&&, __storage<_Tp>>>( - __get_storage(std::forward<_Variant>(__v))); - } - // A helper used to create variadic number of _To types. template<typename _From, typename _To> using _To_type = _To; @@ -597,9 +640,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>) { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); } - template<size_t __index> + template<size_t __index, typename _Tp> static constexpr void - _S_apply_single_alt(auto& __element) + _S_apply_single_alt(_Tp& __element) { using _Alternative = variant_alternative_t<__index, decay_t<_First>>; using _Qualified_storage = __reserved_type_map< @@ -655,23 +698,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>& + constexpr variant_alternative_t<_Np, variant<_Types...>>& get(variant<_Types...>&); template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>&& + constexpr variant_alternative_t<_Np, variant<_Types...>>&& get(variant<_Types...>&&); template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>> const& + constexpr variant_alternative_t<_Np, variant<_Types...>> const& get(const variant<_Types...>&); template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>> const&& + constexpr variant_alternative_t<_Np, variant<_Types...>> const&& get(const variant<_Types...>&&); template<typename _Tp, typename... _Types> - inline _Tp& get(variant<_Types...>& __v) + constexpr inline _Tp& get(variant<_Types...>& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -680,7 +723,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline _Tp&& get(variant<_Types...>&& __v) + constexpr inline _Tp&& get(variant<_Types...>&& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -690,7 +733,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline const _Tp& get(const variant<_Types...>& __v) + constexpr inline const _Tp& get(const variant<_Types...>& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -699,7 +742,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline const _Tp&& get(const variant<_Types...>&& __v) + constexpr inline const _Tp&& get(const variant<_Types...>&& __v) { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -709,7 +752,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<size_t _Np, typename... _Types> - inline add_pointer_t<variant_alternative_t<_Np, variant<_Types...>>> + constexpr inline + add_pointer_t<variant_alternative_t<_Np, variant<_Types...>>> get_if(variant<_Types...>* __ptr) noexcept { using _Alternative_type = variant_alternative_t<_Np, variant<_Types...>>; @@ -717,12 +761,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "The index should be in [0, number of alternatives)"); static_assert(!is_void_v<_Alternative_type>, "_Tp should not be void"); if (__ptr && __ptr->index() == _Np) - return &__detail::__variant::__access<_Alternative_type>(*__ptr); + return &__detail::__variant::__get<_Np>(*__ptr); return nullptr; } template<size_t _Np, typename... _Types> - inline add_pointer_t<const variant_alternative_t<_Np, variant<_Types...>>> + constexpr inline + add_pointer_t<const variant_alternative_t<_Np, variant<_Types...>>> get_if(const variant<_Types...>* __ptr) noexcept { using _Alternative_type = variant_alternative_t<_Np, variant<_Types...>>; @@ -730,12 +775,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "The index should be in [0, number of alternatives)"); static_assert(!is_void_v<_Alternative_type>, "_Tp should not be void"); if (__ptr && __ptr->index() == _Np) - return &__detail::__variant::__access<_Alternative_type>(*__ptr); + return &__detail::__variant::__get<_Np>(*__ptr); return nullptr; } template<typename _Tp, typename... _Types> - inline add_pointer_t<_Tp> get_if(variant<_Types...>* __ptr) noexcept + constexpr inline add_pointer_t<_Tp> + get_if(variant<_Types...>* __ptr) noexcept { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, "T should occur for exactly once in alternatives"); @@ -744,7 +790,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Tp, typename... _Types> - inline add_pointer_t<const _Tp> get_if(const variant<_Types...>* __ptr) + constexpr inline add_pointer_t<const _Tp> + get_if(const variant<_Types...>* __ptr) noexcept { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, @@ -754,64 +801,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename... _Types> - bool operator==(const variant<_Types...>& __lhs, - const variant<_Types...>& __rhs) + constexpr bool operator==(const variant<_Types...>& __lhs, + const variant<_Types...>& __rhs) { - if (__lhs.index() != __rhs.index()) - return false; - - if (__lhs.valueless_by_exception()) - return true; - - using __detail::__variant::__storage; - static constexpr bool (*_S_vtable[])(void*, void*) = - { &__detail::__variant::__erased_equal_to< - const __storage<_Types>&, const __storage<_Types>&>... }; - return _S_vtable[__lhs.index()]( - __detail::__variant::__get_storage(__lhs), - __detail::__variant::__get_storage(__rhs)); + return __lhs._M_equal_to(__rhs, std::index_sequence_for<_Types...>{}); } template<typename... _Types> - inline bool + constexpr inline bool operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs == __rhs); } template<typename... _Types> - inline bool + constexpr inline bool operator<(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { - if (__lhs.index() < __rhs.index()) - return true; - - if (__lhs.index() > __rhs.index()) - return false; - - if (__lhs.valueless_by_exception()) - return false; - - using __detail::__variant::__storage; - static constexpr bool (*_S_vtable[])(void*, void*) = - { &__detail::__variant::__erased_less_than< - const __storage<_Types>&, - const __storage<_Types>&>... }; - return _S_vtable[__lhs.index()]( - __detail::__variant::__get_storage(__lhs), - __detail::__variant::__get_storage(__rhs)); + return __lhs._M_less_than(__rhs, std::index_sequence_for<_Types...>{}); } template<typename... _Types> - inline bool + constexpr inline bool operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return __rhs < __lhs; } template<typename... _Types> - inline bool + constexpr inline bool operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs > __rhs); } template<typename... _Types> - inline bool + constexpr inline bool operator>=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs < __rhs); } @@ -1102,60 +1121,120 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } + private: + template<size_t... __indices> + static constexpr bool + (*_S_equal_to_vtable[])(const variant&, const variant&) = + { &__detail::__variant::__erased_equal_to< + const variant&, __indices>... }; + + template<size_t... __indices> + static constexpr bool + (*_S_less_than_vtable[])(const variant&, const variant&) = + { &__detail::__variant::__erased_less_than< + const variant&, __indices>... }; + + template<size_t... __indices> + constexpr bool + _M_equal_to(const variant& __rhs, + std::index_sequence<__indices...>) const + { + if (this->index() != __rhs.index()) + return false; + + if (this->valueless_by_exception()) + return true; + + return _S_equal_to_vtable<__indices...>[this->index()](*this, __rhs); + } + + template<size_t... __indices> + constexpr inline bool + _M_less_than(const variant& __rhs, + std::index_sequence<__indices...>) const + { + auto __lhs_index = this->index(); + auto __rhs_index = __rhs.index(); + + if (__lhs_index < __rhs_index) + return true; + + if (__lhs_index > __rhs_index) + return false; + + if (this->valueless_by_exception()) + return false; + + return _S_less_than_vtable<__indices...>[__lhs_index](*this, __rhs); + } + + template<size_t _Np, typename _Vp> + friend constexpr decltype(auto) __detail::__variant:: +#if _GLIBCXX_INLINE_VERSION + __7:: // Required due to PR c++/59256 +#endif + __get(_Vp&& __v); + template<typename _Vp> friend void* __detail::__variant:: #if _GLIBCXX_INLINE_VERSION __7:: // Required due to PR c++/59256 #endif __get_storage(_Vp&& __v); + + template<typename... _Tp> + friend constexpr bool + operator==(const variant<_Tp...>& __lhs, + const variant<_Tp...>& __rhs); + + template<typename... _Tp> + friend constexpr bool + operator<(const variant<_Tp...>& __lhs, + const variant<_Tp...>& __rhs); }; template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>& + constexpr variant_alternative_t<_Np, variant<_Types...>>& get(variant<_Types...>& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(__v); + return __detail::__variant::__get<_Np>(__v); } template<size_t _Np, typename... _Types> - variant_alternative_t<_Np, variant<_Types...>>&& + constexpr variant_alternative_t<_Np, variant<_Types...>>&& get(variant<_Types...>&& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(std::move(__v)); + return __detail::__variant::__get<_Np>(std::move(__v)); } template<size_t _Np, typename... _Types> - const variant_alternative_t<_Np, variant<_Types...>>& + constexpr const variant_alternative_t<_Np, variant<_Types...>>& get(const variant<_Types...>& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(__v); + return __detail::__variant::__get<_Np>(__v); } template<size_t _Np, typename... _Types> - const variant_alternative_t<_Np, variant<_Types...>>&& + constexpr const variant_alternative_t<_Np, variant<_Types...>>&& get(const variant<_Types...>&& __v) { static_assert(_Np < sizeof...(_Types), "The index should be in [0, number of alternatives)"); if (__v.index() != _Np) __throw_bad_variant_access("Unexpected index"); - return __detail::__variant::__access< - variant_alternative_t<_Np, variant<_Types...>>>(std::move(__v)); + return __detail::__variant::__get<_Np>(std::move(__v)); } template<typename _Visitor, typename... _Variants> diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index a67b651..ab8ada2 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -51,6 +51,14 @@ struct DefaultNoexcept DefaultNoexcept& operator=(DefaultNoexcept&&) noexcept = default; }; +struct nonliteral +{ + nonliteral() { } + + bool operator<(const nonliteral&) const; + bool operator==(const nonliteral&) const; +}; + void default_ctor() { static_assert(is_default_constructible_v<variant<int, string>>, ""); @@ -175,22 +183,40 @@ void test_get() void test_relational() { { - const variant<int, string> a, b; - (void)(a < b); - (void)(a > b); - (void)(a <= b); - (void)(a == b); - (void)(a != b); - (void)(a >= b); + constexpr variant<int, nonliteral> a(42), b(43); + static_assert((a < b), ""); + static_assert(!(a > b), ""); + static_assert((a <= b), ""); + static_assert(!(a == b), ""); + static_assert((a != b), ""); + static_assert(!(a >= b), ""); } { - const monostate a, b; - (void)(a < b); - (void)(a > b); - (void)(a <= b); - (void)(a == b); - (void)(a != b); - (void)(a >= b); + constexpr variant<int, nonliteral> a(42), b(42); + static_assert(!(a < b), ""); + static_assert(!(a > b), ""); + static_assert((a <= b), ""); + static_assert((a == b), ""); + static_assert(!(a != b), ""); + static_assert((a >= b), ""); + } + { + constexpr variant<int, nonliteral> a(43), b(42); + static_assert(!(a < b), ""); + static_assert((a > b), ""); + static_assert(!(a <= b), ""); + static_assert(!(a == b), ""); + static_assert((a != b), ""); + static_assert((a >= b), ""); + } + { + constexpr monostate a, b; + static_assert(!(a < b), ""); + static_assert(!(a > b), ""); + static_assert((a <= b), ""); + static_assert((a == b), ""); + static_assert(!(a != b), ""); + static_assert((a >= b), ""); } } @@ -262,14 +288,59 @@ void test_constexpr() constexpr literal() = default; }; - struct nonliteral { - nonliteral() { } - }; - constexpr variant<literal, nonliteral> v{}; constexpr variant<literal, nonliteral> v1{in_place_type<literal>}; constexpr variant<literal, nonliteral> v2{in_place_index<0>}; } + + { + constexpr variant<int> a(42); + static_assert(get<0>(a) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<0>(a) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<1>(a) == 42, ""); + } + { + constexpr variant<int> a(42); + static_assert(get<int>(a) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<int>(a) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<int>(a) == 42, ""); + } + { + constexpr variant<int> a(42); + static_assert(get<0>(std::move(a)) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<0>(std::move(a)) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<1>(std::move(a)) == 42, ""); + } + { + constexpr variant<int> a(42); + static_assert(get<int>(std::move(a)) == 42, ""); + } + { + constexpr variant<int, nonliteral> a(42); + static_assert(get<int>(std::move(a)) == 42, ""); + } + { + constexpr variant<nonliteral, int> a(42); + static_assert(get<int>(std::move(a)) == 42, ""); + } } void test_pr77641() [-- Attachment #4: c2.diff --] [-- Type: text/x-patch, Size: 10760 bytes --] commit e1673a8fc7ea3af6b86ce998379fb74626fd7f40 Author: Tim Shen <timshen@google.com> Date: Thu Nov 24 13:35:00 2016 -0800 2016-11-27 Tim Shen <timshen@google.com> * 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. diff --git a/libstdc++-v3/include/bits/enable_special_members.h b/libstdc++-v3/include/bits/enable_special_members.h index 07c6c99..4f4477b 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<typename _Tag> 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<typename _Tag> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index a961a05..fa1e654 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -330,14 +330,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } template<size_t... __indices> - 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_<is_nothrow_move_constructible<_Types>..., @@ -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<size_t _Np, typename _Tp> + struct _Base_dedup : public _Tp { }; + + template<typename _Variant, typename __indices> + struct _Variant_hash_base; + + template<typename... _Types, size_t... __indices> + struct _Variant_hash_base<variant<_Types...>, + std::index_sequence<__indices...>> + : _Base_dedup<__indices, __poison_hash<remove_const_t<_Types>>>... { }; + _GLIBCXX_END_NAMESPACE_VERSION } // namespace __variant } // namespace __detail @@ -858,8 +883,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return false; } template<typename... _Types> - inline enable_if_t<__and_<is_move_constructible<_Types>..., - 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<typename _Tp, typename... _Args> - void emplace(_Args&&... __args) + enable_if_t<is_constructible_v<_Tp, _Args...> && __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<typename _Tp, typename _Up, typename... _Args> - void emplace(initializer_list<_Up> __il, _Args&&... __args) + enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _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<size_t _Np, typename... _Args> - void emplace(_Args&&... __args) + enable_if_t<is_constructible_v<variant_alternative_t<_Np, variant>, + _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<size_t _Np, typename _Up, typename... _Args> - void emplace(initializer_list<_Up> __il, _Args&&... __args) + enable_if_t<is_constructible_v<variant_alternative_t<_Np, variant>, + 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<variant>) + && is_nothrow_move_constructible_v<variant>) { 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<typename... _Types> struct hash<variant<_Types...>> - : private __poison_hash<remove_const_t<_Types>>... + : 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<decay_t<_Types>>{}(std::declval<_Types>())))) + noexcept((is_nothrow_callable_v<hash<decay_t<_Types>>(_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 ab8ada2..087a17c 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<variant<D, int>> ); void test_swap() { - variant<int, string> a, b; - a.swap(b); - swap(a, b); + static_assert(is_swappable_v<variant<int, string>>, ""); + static_assert(is_swappable_v<variant<MoveCtorOnly>>, ""); + static_assert(!is_swappable_v<variant<AllDeleted>>, ""); } void test_visit() @@ -385,7 +394,8 @@ void test_adl() variant<X> v4{in_place_type<X>, il, x}; } -void test_variant_alternative() { +void test_variant_alternative() +{ static_assert(is_same_v<variant_alternative_t<0, variant<int, string>>, int>, ""); static_assert(is_same_v<variant_alternative_t<1, variant<int, string>>, string>, ""); @@ -393,3 +403,28 @@ void test_variant_alternative() { static_assert(is_same_v<variant_alternative_t<0, volatile variant<int>>, volatile int>, ""); static_assert(is_same_v<variant_alternative_t<0, const volatile variant<int>>, const volatile int>, ""); } + +template<typename V, typename T> + constexpr auto has_type_emplace(int) -> decltype((declval<V>().template emplace<T>(), true)) + { return true; }; + +template<typename V, typename T> + constexpr bool has_type_emplace(...) + { return false; }; + +template<typename V, size_t N> + constexpr auto has_index_emplace(int) -> decltype((declval<V>().template emplace<N>(), true)) + { return true; }; + +template<typename V, size_t T> + constexpr bool has_index_emplace(...) + { return false; }; + +void test_emplace() +{ + static_assert(has_type_emplace<variant<int>, int>(0), ""); + static_assert(!has_type_emplace<variant<long>, int>(0), ""); + static_assert(has_index_emplace<variant<int>, 0>(0), ""); + static_assert(!has_type_emplace<variant<AllDeleted>, AllDeleted>(0), ""); + static_assert(!has_index_emplace<variant<AllDeleted>, 0>(0), ""); +} diff --git a/libstdc++-v3/testsuite/20_util/variant/hash.cc b/libstdc++-v3/testsuite/20_util/variant/hash.cc index 38991ae..64d053f 100644 --- a/libstdc++-v3/testsuite/20_util/variant/hash.cc +++ b/libstdc++-v3/testsuite/20_util/variant/hash.cc @@ -29,6 +29,10 @@ template<class T> auto f(...) -> decltype(std::false_type()); static_assert(!decltype(f<S>(0))::value, ""); +static_assert(!decltype(f<std::variant<S>>(0))::value, ""); +static_assert(!decltype(f<std::variant<S, S>>(0))::value, ""); +static_assert(decltype(f<std::variant<int>>(0))::value, ""); +static_assert(decltype(f<std::variant<int, int>>(0))::value, ""); int main() { [-- Attachment #5: d2.diff --] [-- Type: text/x-patch, Size: 20120 bytes --] commit 7f64577f6f54e820f7a313085d09c5f64caa6c1e Author: Tim Shen <timshen@google.com> Date: Sat Nov 26 20:10:40 2016 -0800 2016-11-27 Tim Shen <timshen@google.com> * include/std/variant (visit): Make visit constexpr. Also cleanup __get_alternative and __storage, since we don't support reference/void alternatives any more. * testsuite/20_util/variant/compile.cc: Add tests. diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index fa1e654..dd6109d 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -41,11 +41,34 @@ #include <bits/functexcept.h> #include <bits/move.h> #include <bits/functional_hash.h> +#include <bits/invoke.h> #include <ext/aligned_buffer.h> namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +namespace __detail +{ +namespace __variant +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + template<size_t _Np, typename... _Types> + struct _Nth_type; + + template<size_t _Np, typename _First, typename... _Rest> + struct _Nth_type<_Np, _First, _Rest...> + : _Nth_type<_Np-1, _Rest...> { }; + + template<typename _First, typename... _Rest> + struct _Nth_type<0, _First, _Rest...> + { using type = _First; }; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace __variant +} // namespace __detail + +_GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename... _Types> class tuple; template<typename... _Types> class variant; @@ -99,6 +122,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr size_t variant_npos = -1; + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>>& + get(variant<_Types...>&); + + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>>&& + get(variant<_Types...>&&); + + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>> const& + get(const variant<_Types...>&); + + template<size_t _Np, typename... _Types> + constexpr variant_alternative_t<_Np, variant<_Types...>> const&& + get(const variant<_Types...>&&); + _GLIBCXX_END_NAMESPACE_VERSION namespace __detail @@ -119,41 +158,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::integral_constant<size_t, is_same_v<_Tp, _First> ? 0 : __index_of_v<_Tp, _Rest...> + 1> {}; - // Extract _From's qualifiers and references and apply it to _To. - // __reserved_type_map<const int&, char> is const char&. - template<typename _From, typename _To> - struct __reserved_type_map_impl - { using type = _To; }; - - template<typename _From, typename _To> - using __reserved_type_map = - typename __reserved_type_map_impl<_From, _To>::type; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<_From&, _To> - { using type = add_lvalue_reference_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<_From&&, _To> - { using type = add_rvalue_reference_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<const _From, _To> - { using type = add_const_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<volatile _From, _To> - { using type = add_volatile_t<__reserved_type_map<_From, _To>>; }; - - template<typename _From, typename _To> - struct __reserved_type_map_impl<const volatile _From, _To> - { using type = add_cv_t<__reserved_type_map<_From, _To>>; }; - - // This abstraction might be useful for future features, - // e.g. boost::recursive_wrapper. - template<typename _Alternative> - using __storage = _Alternative; - // _Uninitialized<T> is guaranteed to be a literal type, even if T is not. // We have to do this, because [basic.types]p10.5.3 (n4606) is not implemented // yet. When it's implemented, _Uninitialized<T> can be changed to the alias @@ -210,16 +214,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __gnu_cxx::__aligned_membuf<_Type> _M_storage; }; - // Given a qualified storage type, return the desired reference. - // For example, variant<int>&& stores the int as __storage<int>, and - // _Qualified_storage will be __storage<int>&&. - template<typename _Qualified_storage> - decltype(auto) - __get_alternative(void* __ptr) + template<typename _Ref> + _Ref __ref_cast(void* __ptr) { - using _Storage = decay_t<_Qualified_storage>; - return __reserved_type_map<_Qualified_storage, _Storage>( - *static_cast<_Storage*>(__ptr)); + return static_cast<_Ref>(*static_cast<remove_reference_t<_Ref>*>(__ptr)); } template<typename _Union> @@ -242,7 +240,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Lhs, typename _Rhs> constexpr void __erased_ctor(void* __lhs, void* __rhs) - { ::new (__lhs) decay_t<_Lhs>(__get_alternative<_Rhs>(__rhs)); } + { ::new (__lhs) remove_reference_t<_Lhs>(__ref_cast<_Rhs>(__rhs)); } template<typename _Variant, size_t _Np> constexpr void @@ -256,14 +254,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Lhs, typename _Rhs> constexpr void __erased_assign(void* __lhs, void* __rhs) - { __get_alternative<_Lhs>(__lhs) = __get_alternative<_Rhs>(__rhs); } + { __ref_cast<_Lhs>(__lhs) = __ref_cast<_Rhs>(__rhs); } template<typename _Lhs, typename _Rhs> constexpr void __erased_swap(void* __lhs, void* __rhs) { using std::swap; - swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs)); + swap(__ref_cast<_Lhs>(__lhs), __ref_cast<_Rhs>(__rhs)); } template<typename _Variant, size_t _Np> @@ -285,7 +283,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> constexpr size_t __erased_hash(void* __t) - { return std::hash<decay_t<_Tp>>{}(__get_alternative<_Tp>(__t)); } + { + return std::hash<remove_cv_t<remove_reference_t<_Tp>>>{}( + __ref_cast<_Tp>(__t)); + } // Defines members and ctors. template<typename... _Types> @@ -389,8 +390,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_ctor<__storage<_Types>&, - const __storage<_Types>&>... }; + { &__erased_ctor<_Types&, const _Types&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); this->_M_index = __rhs._M_index; } @@ -402,7 +402,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_ctor<__storage<_Types>&, __storage<_Types>&&>... }; + { &__erased_ctor<_Types&, _Types&&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); this->_M_index = __rhs._M_index; } @@ -422,8 +422,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_assign<__storage<_Types>&, - const __storage<_Types>&>... }; + { &__erased_assign<_Types&, const _Types&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); } } @@ -469,8 +468,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__rhs._M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__erased_assign<__storage<_Types>&, - __storage<_Types>&&>... }; + { &__erased_assign<_Types&, _Types&&>... }; _S_vtable[__rhs._M_index](_M_storage(), __rhs._M_storage()); } } @@ -555,20 +553,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void* __get_storage(_Variant&& __v) { return __v._M_storage(); } - // A helper used to create variadic number of _To types. - template<typename _From, typename _To> - using _To_type = _To; - - // Call the actual visitor. - // _Args are qualified storage types. - template<typename _Visitor, typename... _Args> - decltype(auto) - __visit_invoke(_Visitor&& __visitor, _To_type<_Args, void*>... __ptrs) - { - return std::forward<_Visitor>(__visitor)( - __get_alternative<_Args>(__ptrs)...); - } - // Used for storing multi-dimensional vtable. template<typename _Tp, size_t... _Dimensions> struct _Multi_array @@ -592,108 +576,115 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; // Creates a multi-dimensional vtable recursively. - // _Variant_tuple is initially the input from visit(), and gets gradually - // consumed. - // _Arg_tuple is enumerated alternative sequence, represented by a - // qualified storage. // // For example, // visit([](auto, auto){}, - // variant<int, char>(), - // variant<float, double, long double>()) + // variant<int, char>(), // typedef'ed as V1 + // variant<float, double, long double>()) // typedef'ed as V2 // will trigger instantiations of: - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 2, 3>, - // tuple<variant<int, char>, - // variant<float, double, long double>>, - // tuple<>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 3>, - // tuple<variant<float, double, long double>>, - // tuple<int>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<int, float>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<int, double>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<int, long double>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*), 3>, - // tuple<variant<float, double, long double>>, - // tuple<char>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<char, float>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<char, double>> - // __gen_vtable_impl<_Multi_array<void(*)(void*, void*)>, - // tuple<>, - // tuple<char, long double>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 2, 3>, + // tuple<V1&&, V2&&>, std::index_sequence<>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>, + // tuple<V1&&, V2&&>, std::index_sequence<0>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<0, 0>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<0, 1>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<0, 2>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>, + // tuple<V1&&, V2&&>, std::index_sequence<1>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<1, 0>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<1, 1>> + // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>, + // tuple<V1&&, V2&&>, std::index_sequence<1, 2>> // The returned multi-dimensional vtable can be fast accessed by the visitor // using index calculation. - template<typename _Array_type, typename _Variant_tuple, typename _Arg_tuple> + template<typename _Array_type, typename _Variant_tuple, typename _Index_seq> struct __gen_vtable_impl; - template<typename _Array_type, typename _First, typename... _Rest, - typename... _Args> - struct __gen_vtable_impl<_Array_type, tuple<_First, _Rest...>, - tuple<_Args...>> + template<typename _Result_type, typename _Visitor, size_t... __unused, + typename... _Variants, size_t... __indices> + struct __gen_vtable_impl< + _Multi_array<_Result_type (*)(_Visitor, _Variants...), __unused...>, + tuple<_Variants...>, std::index_sequence<__indices...>> { + using _Next = + remove_reference_t<typename _Nth_type<sizeof...(__indices), + _Variants...>::type>; + using _Array_type = + _Multi_array<_Result_type (*)(_Visitor, _Variants...), __unused...>; + static constexpr _Array_type _S_apply() { _Array_type __vtable{}; _S_apply_all_alts( - __vtable, make_index_sequence<variant_size_v<decay_t<_First>>>()); + __vtable, make_index_sequence<variant_size_v<_Next>>()); return __vtable; } - template<size_t... __indices> + template<size_t... __var_indices> static constexpr void - _S_apply_all_alts(_Array_type& __vtable, index_sequence<__indices...>) - { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), ...); } + _S_apply_all_alts(_Array_type& __vtable, + std::index_sequence<__var_indices...>) + { + (_S_apply_single_alt<__var_indices>( + __vtable._M_arr[__var_indices]), ...); + } template<size_t __index, typename _Tp> static constexpr void _S_apply_single_alt(_Tp& __element) { - using _Alternative = variant_alternative_t<__index, decay_t<_First>>; - using _Qualified_storage = __reserved_type_map< - _First, __storage<_Alternative>>; + using _Alternative = variant_alternative_t<__index, _Next>; __element = __gen_vtable_impl< - decay_t<decltype(__element)>, tuple<_Rest...>, - tuple<_Args..., _Qualified_storage>>::_S_apply(); + remove_reference_t< + decltype(__element)>, tuple<_Variants...>, + std::index_sequence<__indices..., __index>>::_S_apply(); } }; - template<typename _Result_type, typename _Visitor, typename... _Args> + template<typename _Result_type, typename _Visitor, typename... _Variants, + size_t... __indices> struct __gen_vtable_impl< - _Multi_array<_Result_type (*)(_Visitor, _To_type<_Args, void*>...)>, - tuple<>, tuple<_Args...>> + _Multi_array<_Result_type (*)(_Visitor, _Variants...)>, + tuple<_Variants...>, std::index_sequence<__indices...>> { using _Array_type = - _Multi_array<_Result_type (*)(_Visitor&&, _To_type<_Args, void*>...)>; + _Multi_array<_Result_type (*)(_Visitor&&, _Variants...)>; + + decltype(auto) + static constexpr __visit_invoke(_Visitor&& __visitor, _Variants... __vars) + { + return __invoke(std::forward<_Visitor>(__visitor), + std::get<__indices>( + std::forward<_Variants>(__vars))...); + } static constexpr auto _S_apply() - { return _Array_type{&__visit_invoke<_Visitor, _Args...>}; } + { return _Array_type{&__visit_invoke}; } }; template<typename _Result_type, typename _Visitor, typename... _Variants> struct __gen_vtable { - using _Func_ptr = - _Result_type (*)(_Visitor&&, _To_type<_Variants, void*>...); + using _Func_ptr = _Result_type (*)(_Visitor&&, _Variants...); using _Array_type = - _Multi_array<_Func_ptr, variant_size_v<decay_t<_Variants>>...>; + _Multi_array<_Func_ptr, + variant_size_v<remove_reference_t<_Variants>>...>; static constexpr _Array_type _S_apply() { - return __gen_vtable_impl< - _Array_type, tuple<_Variants...>, tuple<>>::_S_apply(); + return __gen_vtable_impl<_Array_type, tuple<_Variants...>, + std::index_sequence<>>::_S_apply(); } + + static constexpr auto _S_vtable = _S_apply(); }; template<size_t _Np, typename _Tp> @@ -722,22 +713,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __v.index() == __detail::__variant::__index_of_v<_Tp, _Types...>; } - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>>& - get(variant<_Types...>&); - - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>>&& - get(variant<_Types...>&&); - - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>> const& - get(const variant<_Types...>&); - - template<size_t _Np, typename... _Types> - constexpr variant_alternative_t<_Np, variant<_Types...>> const&& - get(const variant<_Types...>&&); - template<typename _Tp, typename... _Types> constexpr inline _Tp& get(variant<_Types...>& __v) { @@ -860,7 +835,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return !(__lhs < __rhs); } template<typename _Visitor, typename... _Variants> - decltype(auto) visit(_Visitor&&, _Variants&&...); + constexpr decltype(auto) visit(_Visitor&&, _Variants&&...); struct monostate { }; @@ -965,9 +940,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using __accepted_type = __to_type<__accepted_index<_Tp>>; template<typename _Tp> - using __storage = __detail::__variant::__storage<_Tp>; - - template<typename _Tp> static constexpr size_t __index_of = __detail::__variant::__index_of_v<_Tp, _Types...>; @@ -1127,8 +1099,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (this->_M_valid()) { static constexpr void (*_S_vtable[])(void*, void*) = - { &__detail::__variant::__erased_swap< - __storage<_Types>&, __storage<_Types>&>... }; + { &__detail::__variant::__erased_swap<_Types&, _Types&>... }; _S_vtable[__rhs._M_index](this->_M_storage(), __rhs._M_storage()); } @@ -1268,17 +1239,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _Visitor, typename... _Variants> - decltype(auto) + constexpr decltype(auto) visit(_Visitor&& __visitor, _Variants&&... __variants) { + if ((__variants.valueless_by_exception() || ...)) + __throw_bad_variant_access("Unexpected index"); + using _Result_type = decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...)); - static constexpr auto _S_vtable = - __detail::__variant::__gen_vtable< - _Result_type, _Visitor&&, _Variants&&...>::_S_apply(); - auto __func_ptr = _S_vtable._M_access(__variants.index()...); + + constexpr auto& __vtable = __detail::__variant::__gen_vtable< + _Result_type, _Visitor&&, _Variants&&...>::_S_vtable; + + auto __func_ptr = __vtable._M_access(__variants.index()...); return (*__func_ptr)(std::forward<_Visitor>(__visitor), - __detail::__variant::__get_storage(__variants)...); + std::forward<_Variants>(__variants)...); } template<typename... _Types> @@ -1297,7 +1272,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { namespace __edv = __detail::__variant; static constexpr size_t (*_S_vtable[])(void*) = - { &__edv::__erased_hash<const __edv::__storage<_Types>&>... }; + { &__edv::__erased_hash<const _Types&>... }; return hash<size_t>{}(__t.index()) + _S_vtable[__t.index()](__edv::__get_storage(__t)); } diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index 087a17c..a8ffaea 100644 --- a/libstdc++-v3/testsuite/20_util/variant/compile.cc +++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc @@ -275,6 +275,22 @@ void test_visit() }; visit(Visitor(), variant<int, char>(), variant<float, double>()); } + { + struct Visitor + { + constexpr bool operator()(const int&) { return true; } + constexpr bool operator()(const nonliteral&) { return false; } + }; + static_assert(visit(Visitor(), variant<int, nonliteral>(0)), ""); + } + { + struct Visitor + { + constexpr bool operator()(const int&) { return true; } + constexpr bool operator()(const nonliteral&) { return false; } + }; + static_assert(visit(Visitor(), variant<int, nonliteral>(0)), ""); + } } void test_constexpr() ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-12-03 3:15 ` Tim Shen @ 2016-12-06 10:30 ` Jonathan Wakely 2016-12-06 11:52 ` Tim Shen 0 siblings, 1 reply; 10+ messages in thread From: Jonathan Wakely @ 2016-12-06 10:30 UTC (permalink / raw) To: Tim Shen; +Cc: libstdc++, gcc-patches On 02/12/16 19:14 -0800, Tim Shen wrote: >On Wed, Nov 30, 2016 at 8:27 AM, Jonathan Wakely wrote: >> On 26/11/16 21:38 -0800, Tim Shen wrote: >>> >>> This 4-patch series contains the following in order: >>> >>> a.diff: Remove uses-allocator ctors. They are going away, and removing >>> it reduces the maintenance burden from now on. >> >> >> Yay! less code. > >Yay! Also removed the unused #include. > >> >>> - template<typename _Type, bool __is_literal = >>> std::is_literal_type_v<_Type>> >>> + // _Uninitialized nullify the destructor calls. >> >> >> This wording confused me slightly. How about: >> >> "_Uninitialized makes destructors trivial" > >Change this section of comment to the discussed content. > >> >>> + // This is necessary, since we define _Variadic_union as a recursive >>> union, >>> + // and we don't want to inspect the union members one by one in its >>> dtor, >>> + // it's slow. >> >> >> Please change "it's slow" to "that's slow". > >N/A. > >> >>> + template<typename _Type, bool = std::is_literal_type_v<_Type>> >>> struct _Uninitialized; >> >> >> I'm still unsure that is_literal_type is the right trait here. If it's >> definitely right then we should probably *not* deprecate it in C++17! > >Already discussed. > >> >>> template<typename _Type> >>> struct _Uninitialized<_Type, false> >>> { >>> - constexpr _Uninitialized() = default; >>> - >>> template<typename... _Args> >>> constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) >>> { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); } >>> >>> + const _Type& _M_get() const & >>> + { >>> + return *static_cast<const _Type*>( >>> + static_cast<const void*>(&_M_storage)); >>> + } >>> + >>> + _Type& _M_get() & >>> + { return *static_cast<_Type*>(static_cast<void*>(&_M_storage)); } >>> + >>> + const _Type&& _M_get() const && >>> + { >>> + return std::move(*static_cast<const _Type*>( >>> + static_cast<const void*>(&_M_storage))); >>> + } >>> + >>> + _Type&& _M_get() && >>> + { >>> + return >>> std::move(*static_cast<_Type*>(static_cast<void*>(&_M_storage))); >>> + } >>> + >>> typename std::aligned_storage<sizeof(_Type), alignof(_Type)>::type >>> _M_storage; >> >> >> I think this could use __aligned_membuf, which would reduce the >> alignment requirements for some types (e.g. long long on x86-32). > >Done. > >> >> That would also mean you get the _M_ptr() member so don't need all the >> casts. >> >>> + ~_Variant_storage() >>> + { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } >> >> >> You can use index_sequence_for<_Types...> here. > >Done > >> >>> @@ -598,9 +645,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> _S_apply_all_alts(_Array_type& __vtable, >>> index_sequence<__indices...>) >>> { (_S_apply_single_alt<__indices>(__vtable._M_arr[__indices]), >>> ...); } >>> >>> - template<size_t __index> >>> + template<size_t __index, typename T> >> >> >> This needs to be _Tp not T > >Done. > >> >>> + return __lhs._M_equal_to(__rhs, >>> + >>> std::make_index_sequence<sizeof...(_Types)>{}); >> >> >> Another one that could use index_sequence_for<_Types...> > >Done. > >> >>> + return __lhs._M_less_than(__rhs, >>> + >>> std::make_index_sequence<sizeof...(_Types)>{}); >> >> >> Same again. > >Same again. ;) > >> >> >>> * 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. >>> >>> diff --git a/libstdc++-v3/include/bits/enable_special_members.h >>> b/libstdc++-v3/include/bits/enable_special_members.h >>> index 07c6c99..4f4477b 100644 >>> --- a/libstdc++-v3/include/bits/enable_special_members.h >>> +++ b/libstdc++-v3/include/bits/enable_special_members.h >>> @@ -118,7 +118,8 @@ template<typename _Tag> >>> 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) { } >>> }; >>> >>> + void _M_reset() >>> + { >>> + _M_reset_impl(std::make_index_sequence<sizeof...(_Types)>{}); >>> + _M_index = variant_npos; >>> + } >>> + >>> ~_Variant_storage() >>> - { _M_destroy_impl(std::make_index_sequence<sizeof...(_Types)>{}); } >>> + { _M_reset(); } >> >> >> These can also use index_sequence_for<_Types...> > >Done. > >> >>> @@ -1253,14 +1285,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> >>> template<typename... _Types> >>> struct hash<variant<_Types...>> >>> - : private __poison_hash<remove_const_t<_Types>>... >>> + : private __detail::__variant::_Variant_hash_base< >>> + variant<_Types...>, std::make_index_sequence<sizeof...(_Types)>> >> >> >> And again. > >And again. > >> >>> { >>> using result_type = size_t; >>> using argument_type = variant<_Types...>; >>> >>> size_t >>> operator()(const variant<_Types...>& __t) const >>> - noexcept((... && >>> noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())))) >>> + noexcept((noexcept(hash<decay_t<_Types>>{}(std::declval<_Types>())) >>> + && ...)) >> >> >> This could be >> __and_<is_nothrow_callable<hash<decay_t<_Types>>(_Types)>...> >> but I'm not sure it would be an improvement. The is_callable check is >> expensive, but maybe we need it anyway to correctly disable this >> function if the hash specialization should be posisoned? > >Done. I just realized that is_nothrow_callable also handles crazy >member pointer cases. > >Used fold expression instead of __and_ for consistency. > >> >> >>> @@ -1270,17 +1239,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >>> } >>> >>> template<typename _Visitor, typename... _Variants> >>> - decltype(auto) >>> + constexpr decltype(auto) >>> visit(_Visitor&& __visitor, _Variants&&... __variants) >>> { >>> + if ((__variants.valueless_by_exception() || ...)) >>> + __throw_bad_variant_access("Unexpected index"); >>> + >>> using _Result_type = >>> >>> decltype(std::forward<_Visitor>(__visitor)(get<0>(__variants)...)); >>> - static constexpr auto _S_vtable = >>> + constexpr auto _S_vtable = >> >> >> If this isn't static now it could be called simply __vtable, the _S_ >> prefix is misleading. How many of these _S_vtable variables actually >> need to be static? If they're all trivial types and constexpr then it >> probably doesn't matter either way, there shouldn't be any difference. > >Ah that's an oversight. Moved the static variable out of visit(). >_S_vtable needs to be static, otherwise runtime O(n) assignment will >happen, where n is the size of _S_vtable. This looks good - OK for trunk, thanks! ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-12-06 10:30 ` Jonathan Wakely @ 2016-12-06 11:52 ` Tim Shen 2016-12-06 12:49 ` Jonathan Wakely 0 siblings, 1 reply; 10+ messages in thread From: Tim Shen @ 2016-12-06 11:52 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, gcc-patches On Tue, Dec 6, 2016 at 2:30 AM, Jonathan Wakely wrote: > This looks good - OK for trunk, thanks! Committed. Thanks! -- Regards, Tim Shen ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-12-06 11:52 ` Tim Shen @ 2016-12-06 12:49 ` Jonathan Wakely 2016-12-06 14:37 ` Jonathan Wakely 0 siblings, 1 reply; 10+ messages in thread From: Jonathan Wakely @ 2016-12-06 12:49 UTC (permalink / raw) To: Tim Shen; +Cc: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 204 bytes --] On 06/12/16 03:52 -0800, Tim Shen wrote: >On Tue, Dec 6, 2016 at 2:30 AM, Jonathan Wakely wrote: >> This looks good - OK for trunk, thanks! > >Committed. > >Thanks! ChangeLog dates fixed by this patch. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 1617 bytes --] commit c3651cf0403b826414b8376bb29b4757530f4eec Author: redi <redi@138bc75d-0d04-0410-961f-82ee72b054a4> Date: Tue Dec 6 12:48:54 2016 +0000 Fix libstdc++-v3/ChangeLog dates git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@243299 138bc75d-0d04-0410-961f-82ee72b054a4 diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 47e9abf..b8edb7b 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -9,14 +9,14 @@ Guard with #ifndef __cpp_lib_addressof_constexpr. (operator->()): Use std::__addressof if it's constexpr. -2016-11-27 Tim Shen <timshen@google.com> +2016-12-06 Tim Shen <timshen@google.com> * include/std/variant (visit): Make visit constexpr. Also cleanup __get_alternative and __storage, since we don't support reference/void alternatives any more. * testsuite/20_util/variant/compile.cc: Add tests. -2016-12-07 Tim Shen <timshen@google.com> +2016-12-06 Tim Shen <timshen@google.com> * include/bits/enable_special_members.h: Make _Enable_default_constructor constexpr. @@ -24,13 +24,13 @@ std::hash): Sfinae on emplace and std::swap; handle __poison_hash bases of duplicated types. -2016-12-07 Tim Shen <timshen@google.com> +2016-12-06 Tim Shen <timshen@google.com> * include/std/variant (std::get, operator==): Implement constexpr comparison and get<>. * testsuite/20_util/variant/compile.cc: Tests. -2016-12-07 Tim Shen <timshen@google.com> +2016-12-06 Tim Shen <timshen@google.com> * include/std/variant (__erased_use_alloc_ctor, _Variant_base::_Variant_base, variant::variant): Remove uses-allocator ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-12-06 12:49 ` Jonathan Wakely @ 2016-12-06 14:37 ` Jonathan Wakely 2016-12-06 20:10 ` Tim Shen 0 siblings, 1 reply; 10+ messages in thread From: Jonathan Wakely @ 2016-12-06 14:37 UTC (permalink / raw) To: Tim Shen; +Cc: libstdc++, gcc-patches [-- Attachment #1: Type: text/plain, Size: 449 bytes --] On 06/12/16 12:49 +0000, Jonathan Wakely wrote: >On 06/12/16 03:52 -0800, Tim Shen wrote: >>On Tue, Dec 6, 2016 at 2:30 AM, Jonathan Wakely wrote: >>>This looks good - OK for trunk, thanks! >> >>Committed. >> >>Thanks! > >ChangeLog dates fixed by this patch. And pretty printer fixed by this one. The "vref" test originally testsed variant<string_view&> but is no longer needed, as that isn't supported. Tested x86_64-linux, committed to trunk. [-- Attachment #2: patch.txt --] [-- Type: text/x-patch, Size: 1914 bytes --] commit 2f135ef76712b96e3dd799dfbb17e8d16545b115 Author: Jonathan Wakely <jwakely@redhat.com> Date: Tue Dec 6 14:24:13 2016 +0000 Fix pretty-printer for std::variant * python/libstdcxx/v6/printers.py (StdVariantPrinter): Update for new data member name. * testsuite/libstdc++-prettyprinters/cxx17.cc: Remove redundant test. diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index bad42b4..ff428e8 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -1002,7 +1002,7 @@ class StdVariantPrinter(SingleObjContainerPrinter): visualizer = None else: self.contained_type = alternatives[int(self.index)] - addr = val['_M_union']['_M_first']['_M_storage'].address + addr = val['_M_u']['_M_first']['_M_storage'].address contained_value = addr.cast(self.contained_type.pointer()).dereference() visualizer = gdb.default_visualizer(contained_value) super (StdVariantPrinter, self).__init__(contained_value, visualizer, 'array') diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc index 96be8c7..69c16c1 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc @@ -86,8 +86,6 @@ main() // { dg-final { note-test v3 {std::variant<float, int, std::string_view> [index 1] = {3}} } } variant<float, int, string_view> v4{ str }; // { dg-final { note-test v4 {std::variant<float, int, std::string_view> [index 2] = {"string"}} } } - variant<string_view> vref{str}; -// { dg-final { note-test vref {std::variant<std::string_view> [index 0] = {"string"}} } } map<int, string_view> m{ {1, "one"} }; map<int, string_view>::node_type n0; ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Patches] Add variant constexpr support for visit, comparisons and get 2016-12-06 14:37 ` Jonathan Wakely @ 2016-12-06 20:10 ` Tim Shen 0 siblings, 0 replies; 10+ messages in thread From: Tim Shen @ 2016-12-06 20:10 UTC (permalink / raw) To: Jonathan Wakely; +Cc: libstdc++, gcc-patches On Tue, Dec 6, 2016 at 6:37 AM, Jonathan Wakely <jwakely@redhat.com> wrote: > On 06/12/16 12:49 +0000, Jonathan Wakely wrote: >> >> On 06/12/16 03:52 -0800, Tim Shen wrote: >>> >>> On Tue, Dec 6, 2016 at 2:30 AM, Jonathan Wakely wrote: >>>> >>>> This looks good - OK for trunk, thanks! >>> >>> >>> Committed. >>> >>> Thanks! >> >> >> ChangeLog dates fixed by this patch. > > > And pretty printer fixed by this one. > > The "vref" test originally testsed variant<string_view&> but is no > longer needed, as that isn't supported. > > Tested x86_64-linux, committed to trunk. > Thanks! Just figured out that I need to run prettyprinters.exp (I usually just run "conformance.exp=20_util/variant/*") to catch the failures... -- Regards, Tim Shen ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2016-12-06 20:10 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-11-27 5:38 [Patches] Add variant constexpr support for visit, comparisons and get Tim Shen 2016-11-30 16:27 ` Jonathan Wakely 2016-12-01 3:29 ` Tim Shen 2016-12-01 11:16 ` Jonathan Wakely 2016-12-03 3:15 ` Tim Shen 2016-12-06 10:30 ` Jonathan Wakely 2016-12-06 11:52 ` Tim Shen 2016-12-06 12:49 ` Jonathan Wakely 2016-12-06 14:37 ` Jonathan Wakely 2016-12-06 20:10 ` Tim Shen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).