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
commit 638ecd4cf354d853bb12b089a356df99531f9afa Author: Tim Shen <tims...@google.com> Date: Thu Nov 24 00:56:08 2016 -0800 2016-11-26 Tim Shen <tims...@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();
commit 7c510c3d9ac56684a966ae818a2b0d64d63463c2 Author: Tim Shen <tims...@google.com> Date: Thu Nov 24 12:00:48 2016 -0800 2016-11-26 Tim Shen <tims...@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()
commit e80deb97ce75f6a3057c1115c4511fa3dbfb08d9 Author: Tim Shen <tims...@google.com> Date: Thu Nov 24 13:35:00 2016 -0800 2016-11-27 Tim Shen <tims...@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() {
commit 1503eff1772d0ad7638dad2507d860ea56039714 Author: Tim Shen <tims...@google.com> Date: Sat Nov 26 20:10:40 2016 -0800 2016-11-27 Tim Shen <tims...@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()