From 9189f55908d6655e63fff8d9b9f87ec83d4891e1 Mon Sep 17 00:00:00 2001 From: Tim Shen Date: Tue, 6 Dec 2016 11:20:13 +0000 Subject: [PATCH] variant (std::get, operator==): Implement constexpr comparison and get<>. * include/std/variant (std::get, operator==): Implement constexpr comparison and get<>. * testsuite/20_util/variant/compile.cc: Tests. From-SVN: r243293 --- libstdc++-v3/ChangeLog | 6 + libstdc++-v3/include/std/variant | 401 +++++++++++------- .../testsuite/20_util/variant/compile.cc | 107 ++++- 3 files changed, 335 insertions(+), 179 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index ecda02675cf..8a3ab43d665 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,9 @@ +2016-12-07 Tim Shen + + * include/std/variant (std::get, operator==): Implement constexpr + comparison and get<>. + * testsuite/20_util/variant/compile.cc: Tests. + 2016-12-07 Tim Shen * include/std/variant (__erased_use_alloc_ctor, diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index 32c0dc3c07a..a961a058a56 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -41,6 +41,7 @@ #include #include #include +#include namespace std _GLIBCXX_VISIBILITY(default) { @@ -153,33 +154,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template using __storage = _Alternative; - template> + // _Uninitialized 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 can be changed to the alias + // to T, therefore equivalent to being removed entirely. + // + // Another reason we may not want to remove _Uninitialzied may be that, we + // want _Uninitialized to be trivially destructible, no matter whether T + // is; but we will see. + template> struct _Uninitialized; template struct _Uninitialized<_Type, true> { - constexpr _Uninitialized() = default; - template 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 struct _Uninitialized<_Type, false> { - constexpr _Uninitialized() = default; - template constexpr _Uninitialized(in_place_index_t<0>, _Args&&... __args) { ::new (&_M_storage) _Type(std::forward<_Args>(__args)...); } - typename std::aligned_storage::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 + constexpr decltype(auto) __get(in_place_index_t<0>, _Union&& __u) + { return std::forward<_Union>(__u)._M_first._M_get(); } + + template + 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 + 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 @@ -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 + template 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; + __element.~_Type(); } template @@ -223,90 +266,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION swap(__get_alternative<_Lhs>(__lhs), __get_alternative<_Rhs>(__rhs)); } - template + template 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 + template 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 constexpr size_t __erased_hash(void* __t) { return std::hash>{}(__get_alternative<_Tp>(__t)); } + // Defines members and ctors. template - struct _Variant_base; + union _Variadic_union { }; - template - struct _Variant_storage - { constexpr _Variant_storage() = default; }; - - // Use recursive unions to implement a trivially destructible variant. template - struct _Variant_storage<_First, _Rest...> + union _Variadic_union<_First, _Rest...> { - constexpr _Variant_storage() = default; + constexpr _Variadic_union() : _M_rest() { } + + template + constexpr _Variadic_union(in_place_index_t<0>, _Args&&... __args) + : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) + { } + + template + 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 + struct _Variant_storage; + + template + struct _Variant_storage + { + template + static constexpr void (*_S_vtable[])(const _Variant_storage&) = + { &__erased_dtor... }; + + constexpr _Variant_storage() : _M_index(variant_npos) { } template 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 + 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( - static_cast(std::addressof(_M_union._M_first._M_storage))); - } + ~_Variant_storage() + { _M_destroy_impl(std::index_sequence_for<_Types...>{}); } - union _Union - { - constexpr _Union() {}; - - template - constexpr _Union(in_place_index_t<0>, _Args&&... __args) - : _M_first(in_place_index<0>, std::forward<_Args>(__args)...) - { } - - template> - 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 - struct _Dtor_mixin + template + struct _Variant_storage { - ~_Dtor_mixin() - { static_cast<_Derived*>(this)->_M_destroy(); } - }; + constexpr _Variant_storage() : _M_index(variant_npos) { } - template - struct _Dtor_mixin<_Derived, true> - { - ~_Dtor_mixin() = default; + template + 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 struct _Variant_base : - _Variant_storage<_Types...>, - _Dtor_mixin<_Variant_base<_Types...>, - __and_...>::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_...>::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 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_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(static_cast( + 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 - 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 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 + template 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 - variant_alternative_t<_Np, variant<_Types...>>& + constexpr variant_alternative_t<_Np, variant<_Types...>>& get(variant<_Types...>&); template - variant_alternative_t<_Np, variant<_Types...>>&& + constexpr variant_alternative_t<_Np, variant<_Types...>>&& get(variant<_Types...>&&); template - variant_alternative_t<_Np, variant<_Types...>> const& + constexpr variant_alternative_t<_Np, variant<_Types...>> const& get(const variant<_Types...>&); template - variant_alternative_t<_Np, variant<_Types...>> const&& + constexpr variant_alternative_t<_Np, variant<_Types...>> const&& get(const variant<_Types...>&&); template - 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 - 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 - 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 - 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 - inline add_pointer_t>> + constexpr inline + add_pointer_t>> 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 - inline add_pointer_t>> + constexpr inline + add_pointer_t>> 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 - 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 - inline add_pointer_t get_if(const variant<_Types...>* __ptr) + constexpr inline add_pointer_t + get_if(const variant<_Types...>* __ptr) noexcept { static_assert(__detail::__variant::__exactly_once<_Tp, _Types...>, @@ -754,64 +801,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template - 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 - inline bool + constexpr inline bool operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs == __rhs); } template - 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 - inline bool + constexpr inline bool operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return __rhs < __lhs; } template - inline bool + constexpr inline bool operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { return !(__lhs > __rhs); } template - 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 + static constexpr bool + (*_S_equal_to_vtable[])(const variant&, const variant&) = + { &__detail::__variant::__erased_equal_to< + const variant&, __indices>... }; + + template + static constexpr bool + (*_S_less_than_vtable[])(const variant&, const variant&) = + { &__detail::__variant::__erased_less_than< + const variant&, __indices>... }; + + template + 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 + 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 + friend constexpr decltype(auto) __detail::__variant:: +#if _GLIBCXX_INLINE_VERSION + __7:: // Required due to PR c++/59256 +#endif + __get(_Vp&& __v); + template friend void* __detail::__variant:: #if _GLIBCXX_INLINE_VERSION __7:: // Required due to PR c++/59256 #endif __get_storage(_Vp&& __v); + + template + friend constexpr bool + operator==(const variant<_Tp...>& __lhs, + const variant<_Tp...>& __rhs); + + template + friend constexpr bool + operator<(const variant<_Tp...>& __lhs, + const variant<_Tp...>& __rhs); }; template - 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 - 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 - 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 - 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 diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc index a67b651586b..ab8ada2e919 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>, ""); @@ -175,22 +183,40 @@ void test_get() void test_relational() { { - const variant a, b; - (void)(a < b); - (void)(a > b); - (void)(a <= b); - (void)(a == b); - (void)(a != b); - (void)(a >= b); + constexpr variant 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 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 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 v{}; constexpr variant v1{in_place_type}; constexpr variant v2{in_place_index<0>}; } + + { + constexpr variant a(42); + static_assert(get<0>(a) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get<0>(a) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get<1>(a) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get(a) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get(a) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get(a) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get<0>(std::move(a)) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get<0>(std::move(a)) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get<1>(std::move(a)) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get(std::move(a)) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get(std::move(a)) == 42, ""); + } + { + constexpr variant a(42); + static_assert(get(std::move(a)) == 42, ""); + } } void test_pr77641() -- 2.30.2