mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 18:46:05 +00:00
[libc++] Make constexpr std::variant
. Implement P2231R1 (#83335)
Fixes #86686
This commit is contained in:
parent
331f22af4b
commit
52271a5c11
@ -52,6 +52,7 @@ Implemented Papers
|
||||
- P3029R1 - Better ``mdspan``'s CTAD
|
||||
- P2387R3 - Pipe support for user-defined range adaptors
|
||||
- P2713R1 - Escaping improvements in ``std::format``
|
||||
- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
|
||||
|
||||
Improvements and New Features
|
||||
-----------------------------
|
||||
|
@ -47,7 +47,6 @@ Paper Status
|
||||
.. [#note-P0619] P0619: Only sections D.8, D.9, D.10 and D.13 are implemented. Sections D.4, D.7, D.11, and D.12 remain undone.
|
||||
.. [#note-P0883.1] P0883: shared_ptr and floating-point changes weren't applied as they themselves aren't implemented yet.
|
||||
.. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0.
|
||||
.. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet.
|
||||
.. [#note-P0660] P0660: The paper is implemented but the features are experimental and can be enabled via ``-fexperimental-library``.
|
||||
.. [#note-P0355] P0355: The implementation status is:
|
||||
|
||||
|
@ -192,7 +192,7 @@
|
||||
"`P2106R0 <https://wg21.link/P2106R0>`__","LWG","Alternative wording for GB315 and GB316","Prague","|Complete|","15.0","|ranges|"
|
||||
"`P2116R0 <https://wg21.link/P2116R0>`__","LWG","Remove tuple-like protocol support from fixed-extent span","Prague","|Complete|","11.0"
|
||||
"","","","","","",""
|
||||
"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0"
|
||||
"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Complete|","19.0"
|
||||
"`P2325R3 <https://wg21.link/P2325R3>`__","LWG","Views should not be required to be default constructible","June 2021","|Complete|","16.0","|ranges|"
|
||||
"`P2210R2 <https://wg21.link/P2210R2>`__","LWG","Superior String Splitting","June 2021","|Complete|","16.0","|ranges|"
|
||||
"`P2216R3 <https://wg21.link/P2216R3>`__","LWG","std::format improvements","June 2021","|Complete|","15.0"
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
@ -42,26 +42,28 @@ namespace std {
|
||||
in_place_index_t<I>, initializer_list<U>, Args&&...);
|
||||
|
||||
// 20.7.2.2, destructor
|
||||
~variant();
|
||||
constexpr ~variant(); // constexpr since c++20
|
||||
|
||||
// 20.7.2.3, assignment
|
||||
constexpr variant& operator=(const variant&);
|
||||
constexpr variant& operator=(variant&&) noexcept(see below);
|
||||
|
||||
template <class T> variant& operator=(T&&) noexcept(see below);
|
||||
template <class T>
|
||||
constexpr variant& operator=(T&&) noexcept(see below); // constexpr since c++20
|
||||
|
||||
// 20.7.2.4, modifiers
|
||||
template <class T, class... Args>
|
||||
T& emplace(Args&&...);
|
||||
constexpr T& emplace(Args&&...); // constexpr since c++20
|
||||
|
||||
template <class T, class U, class... Args>
|
||||
T& emplace(initializer_list<U>, Args&&...);
|
||||
constexpr T& emplace(initializer_list<U>, Args&&...); // constexpr since c++20
|
||||
|
||||
template <size_t I, class... Args>
|
||||
variant_alternative_t<I, variant>& emplace(Args&&...);
|
||||
constexpr variant_alternative_t<I, variant>& emplace(Args&&...); // constexpr since c++20
|
||||
|
||||
template <size_t I, class U, class... Args>
|
||||
variant_alternative_t<I, variant>& emplace(initializer_list<U>, Args&&...);
|
||||
constexpr variant_alternative_t<I, variant>&
|
||||
emplace(initializer_list<U>, Args&&...); // constexpr since c++20
|
||||
|
||||
// 20.7.2.5, value status
|
||||
constexpr bool valueless_by_exception() const noexcept;
|
||||
@ -221,6 +223,7 @@ namespace std {
|
||||
#include <__functional/operations.h>
|
||||
#include <__functional/unary_function.h>
|
||||
#include <__memory/addressof.h>
|
||||
#include <__memory/construct_at.h>
|
||||
#include <__tuple/find_index.h>
|
||||
#include <__tuple/sfinae_helpers.h>
|
||||
#include <__type_traits/add_const.h>
|
||||
@ -663,7 +666,8 @@ private:
|
||||
|
||||
template <size_t _Index, class _Tp>
|
||||
struct _LIBCPP_TEMPLATE_VIS __alt {
|
||||
using __value_type = _Tp;
|
||||
using __value_type = _Tp;
|
||||
static constexpr size_t __index = _Index;
|
||||
|
||||
template <class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI explicit constexpr __alt(in_place_t, _Args&&... __args)
|
||||
@ -678,7 +682,7 @@ union _LIBCPP_TEMPLATE_VIS __union;
|
||||
template <_Trait _DestructibleTrait, size_t _Index>
|
||||
union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {};
|
||||
|
||||
# define _LIBCPP_VARIANT_UNION(destructible_trait, destructor) \
|
||||
# define _LIBCPP_VARIANT_UNION(destructible_trait, destructor_definition) \
|
||||
template <size_t _Index, class _Tp, class... _Types> \
|
||||
union _LIBCPP_TEMPLATE_VIS __union<destructible_trait, _Index, _Tp, _Types...> { \
|
||||
public: \
|
||||
@ -692,13 +696,11 @@ union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {};
|
||||
_LIBCPP_HIDE_FROM_ABI explicit constexpr __union(in_place_index_t<_Ip>, _Args&&... __args) \
|
||||
: __tail(in_place_index<_Ip - 1>, std::forward<_Args>(__args)...) {} \
|
||||
\
|
||||
__union(const __union&) = default; \
|
||||
__union(__union&&) = default; \
|
||||
\
|
||||
destructor; \
|
||||
\
|
||||
__union& operator=(const __union&) = default; \
|
||||
__union& operator=(__union&&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __union(const __union&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __union(__union&&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __union& operator=(const __union&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __union& operator=(__union&&) = default; \
|
||||
destructor_definition; \
|
||||
\
|
||||
private: \
|
||||
char __dummy; \
|
||||
@ -708,10 +710,11 @@ union _LIBCPP_TEMPLATE_VIS __union<_DestructibleTrait, _Index> {};
|
||||
friend struct __access::__union; \
|
||||
}
|
||||
|
||||
_LIBCPP_VARIANT_UNION(_Trait::_TriviallyAvailable, ~__union() = default);
|
||||
_LIBCPP_VARIANT_UNION(_Trait::_TriviallyAvailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() = default);
|
||||
_LIBCPP_VARIANT_UNION(
|
||||
_Trait::_Available, _LIBCPP_HIDE_FROM_ABI ~__union() {} _LIBCPP_EAT_SEMICOLON);
|
||||
_LIBCPP_VARIANT_UNION(_Trait::_Unavailable, ~__union() = delete);
|
||||
_Trait::_Available, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() {} _LIBCPP_EAT_SEMICOLON);
|
||||
_LIBCPP_VARIANT_UNION(_Trait::_Unavailable, _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__union() = delete);
|
||||
|
||||
# undef _LIBCPP_VARIANT_UNION
|
||||
|
||||
@ -754,7 +757,7 @@ protected:
|
||||
template <class _Traits, _Trait = _Traits::__destructible_trait>
|
||||
class _LIBCPP_TEMPLATE_VIS __dtor;
|
||||
|
||||
# define _LIBCPP_VARIANT_DESTRUCTOR(destructible_trait, destructor, destroy) \
|
||||
# define _LIBCPP_VARIANT_DESTRUCTOR(destructible_trait, destructor_definition, destroy) \
|
||||
template <class... _Types> \
|
||||
class _LIBCPP_TEMPLATE_VIS __dtor<__traits<_Types...>, destructible_trait> \
|
||||
: public __base<destructible_trait, _Types...> { \
|
||||
@ -764,28 +767,27 @@ class _LIBCPP_TEMPLATE_VIS __dtor;
|
||||
public: \
|
||||
using __base_type::__base_type; \
|
||||
using __base_type::operator=; \
|
||||
\
|
||||
__dtor(const __dtor&) = default; \
|
||||
__dtor(__dtor&&) = default; \
|
||||
__dtor& operator=(const __dtor&) = default; \
|
||||
__dtor& operator=(__dtor&&) = default; \
|
||||
destructor; \
|
||||
_LIBCPP_HIDE_FROM_ABI __dtor(const __dtor&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __dtor(__dtor&&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __dtor& operator=(const __dtor&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __dtor& operator=(__dtor&&) = default; \
|
||||
destructor_definition; \
|
||||
\
|
||||
protected: \
|
||||
inline _LIBCPP_HIDE_FROM_ABI destroy; \
|
||||
destroy; \
|
||||
}
|
||||
|
||||
_LIBCPP_VARIANT_DESTRUCTOR(
|
||||
_Trait::_TriviallyAvailable,
|
||||
~__dtor() = default, //
|
||||
_LIBCPP_HIDE_FROM_ABI void __destroy() noexcept {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = default,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept {
|
||||
this->__index = __variant_npos<__index_t>;
|
||||
} _LIBCPP_EAT_SEMICOLON);
|
||||
|
||||
_LIBCPP_VARIANT_DESTRUCTOR(
|
||||
_Trait::_Available,
|
||||
_LIBCPP_HIDE_FROM_ABI ~__dtor() { __destroy(); } _LIBCPP_EAT_SEMICOLON,
|
||||
_LIBCPP_HIDE_FROM_ABI void __destroy() noexcept {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() { __destroy(); } _LIBCPP_EAT_SEMICOLON,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept {
|
||||
if (!this->valueless_by_exception()) {
|
||||
__visitation::__base::__visit_alt(
|
||||
[](auto& __alt) noexcept {
|
||||
@ -797,7 +799,9 @@ _LIBCPP_VARIANT_DESTRUCTOR(
|
||||
this->__index = __variant_npos<__index_t>;
|
||||
} _LIBCPP_EAT_SEMICOLON);
|
||||
|
||||
_LIBCPP_VARIANT_DESTRUCTOR(_Trait::_Unavailable, ~__dtor() = delete, void __destroy() noexcept = delete);
|
||||
_LIBCPP_VARIANT_DESTRUCTOR(_Trait::_Unavailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__dtor() = delete,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __destroy() noexcept = delete);
|
||||
|
||||
# undef _LIBCPP_VARIANT_DESTRUCTOR
|
||||
|
||||
@ -810,23 +814,18 @@ public:
|
||||
using __base_type::operator=;
|
||||
|
||||
protected:
|
||||
template <size_t _Ip, class _Tp, class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI static _Tp& __construct_alt(__alt<_Ip, _Tp>& __a, _Args&&... __args) {
|
||||
::new ((void*)std::addressof(__a)) __alt<_Ip, _Tp>(in_place, std::forward<_Args>(__args)...);
|
||||
return __a.__value;
|
||||
}
|
||||
|
||||
template <class _Rhs>
|
||||
_LIBCPP_HIDE_FROM_ABI static void __generic_construct(__ctor& __lhs, _Rhs&& __rhs) {
|
||||
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX20 void __generic_construct(__ctor& __lhs, _Rhs&& __rhs) {
|
||||
__lhs.__destroy();
|
||||
if (!__rhs.valueless_by_exception()) {
|
||||
auto __rhs_index = __rhs.index();
|
||||
__visitation::__base::__visit_alt_at(
|
||||
__rhs_index,
|
||||
[](auto& __lhs_alt, auto&& __rhs_alt) {
|
||||
__construct_alt(__lhs_alt, std::forward<decltype(__rhs_alt)>(__rhs_alt).__value);
|
||||
[&__lhs](auto&& __rhs_alt) {
|
||||
std::__construct_at(std::addressof(__lhs.__data),
|
||||
in_place_index<__decay_t<decltype(__rhs_alt)>::__index>,
|
||||
std::forward<decltype(__rhs_alt)>(__rhs_alt).__value);
|
||||
},
|
||||
__lhs,
|
||||
std::forward<_Rhs>(__rhs));
|
||||
__lhs.__index = __rhs_index;
|
||||
}
|
||||
@ -836,7 +835,7 @@ protected:
|
||||
template <class _Traits, _Trait = _Traits::__move_constructible_trait>
|
||||
class _LIBCPP_TEMPLATE_VIS __move_constructor;
|
||||
|
||||
# define _LIBCPP_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, move_constructor) \
|
||||
# define _LIBCPP_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, move_constructor_definition) \
|
||||
template <class... _Types> \
|
||||
class _LIBCPP_TEMPLATE_VIS __move_constructor<__traits<_Types...>, move_constructible_trait> \
|
||||
: public __ctor<__traits<_Types...>> { \
|
||||
@ -846,32 +845,35 @@ class _LIBCPP_TEMPLATE_VIS __move_constructor;
|
||||
using __base_type::__base_type; \
|
||||
using __base_type::operator=; \
|
||||
\
|
||||
__move_constructor(const __move_constructor&) = default; \
|
||||
~__move_constructor() = default; \
|
||||
__move_constructor& operator=(const __move_constructor&) = default; \
|
||||
__move_constructor& operator=(__move_constructor&&) = default; \
|
||||
move_constructor; \
|
||||
_LIBCPP_HIDE_FROM_ABI __move_constructor(const __move_constructor&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI ~__move_constructor() = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __move_constructor& operator=(const __move_constructor&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __move_constructor& operator=(__move_constructor&&) = default; \
|
||||
move_constructor_definition; \
|
||||
}
|
||||
|
||||
_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(_Trait::_TriviallyAvailable,
|
||||
__move_constructor(__move_constructor&& __that) = default);
|
||||
_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(
|
||||
_Trait::_TriviallyAvailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) = default);
|
||||
|
||||
_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(
|
||||
_Trait::_Available,
|
||||
_LIBCPP_HIDE_FROM_ABI __move_constructor(__move_constructor&& __that) noexcept(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&& __that) noexcept(
|
||||
__all<is_nothrow_move_constructible_v<_Types>...>::value)
|
||||
: __move_constructor(__valueless_t{}) {
|
||||
this->__generic_construct(*this, std::move(__that));
|
||||
} _LIBCPP_EAT_SEMICOLON);
|
||||
|
||||
_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(_Trait::_Unavailable, __move_constructor(__move_constructor&&) = delete);
|
||||
_LIBCPP_VARIANT_MOVE_CONSTRUCTOR(
|
||||
_Trait::_Unavailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_constructor(__move_constructor&&) = delete);
|
||||
|
||||
# undef _LIBCPP_VARIANT_MOVE_CONSTRUCTOR
|
||||
|
||||
template <class _Traits, _Trait = _Traits::__copy_constructible_trait>
|
||||
class _LIBCPP_TEMPLATE_VIS __copy_constructor;
|
||||
|
||||
# define _LIBCPP_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, copy_constructor) \
|
||||
# define _LIBCPP_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, copy_constructor_definition) \
|
||||
template <class... _Types> \
|
||||
class _LIBCPP_TEMPLATE_VIS __copy_constructor<__traits<_Types...>, copy_constructible_trait> \
|
||||
: public __move_constructor<__traits<_Types...>> { \
|
||||
@ -881,21 +883,25 @@ class _LIBCPP_TEMPLATE_VIS __copy_constructor;
|
||||
using __base_type::__base_type; \
|
||||
using __base_type::operator=; \
|
||||
\
|
||||
__copy_constructor(__copy_constructor&&) = default; \
|
||||
~__copy_constructor() = default; \
|
||||
__copy_constructor& operator=(const __copy_constructor&) = default; \
|
||||
__copy_constructor& operator=(__copy_constructor&&) = default; \
|
||||
copy_constructor; \
|
||||
} // namespace __variant_detail
|
||||
|
||||
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(_Trait::_TriviallyAvailable,
|
||||
__copy_constructor(const __copy_constructor& __that) = default);
|
||||
_LIBCPP_HIDE_FROM_ABI __copy_constructor(__copy_constructor&&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI ~__copy_constructor() = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __copy_constructor& operator=(const __copy_constructor&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __copy_constructor& operator=(__copy_constructor&&) = default; \
|
||||
copy_constructor_definition; \
|
||||
}
|
||||
|
||||
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
|
||||
_Trait::_Available, _LIBCPP_HIDE_FROM_ABI __copy_constructor(const __copy_constructor& __that)
|
||||
_Trait::_TriviallyAvailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) = default);
|
||||
|
||||
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
|
||||
_Trait::_Available,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that)
|
||||
: __copy_constructor(__valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON);
|
||||
|
||||
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(_Trait::_Unavailable, __copy_constructor(const __copy_constructor&) = delete);
|
||||
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
|
||||
_Trait::_Unavailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor&) = delete);
|
||||
|
||||
# undef _LIBCPP_VARIANT_COPY_CONSTRUCTOR
|
||||
|
||||
@ -908,22 +914,24 @@ public:
|
||||
using __base_type::operator=;
|
||||
|
||||
template <size_t _Ip, class... _Args>
|
||||
_LIBCPP_HIDE_FROM_ABI auto& __emplace(_Args&&... __args) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto& __emplace(_Args&&... __args) {
|
||||
this->__destroy();
|
||||
auto& __res = this->__construct_alt(__access::__base::__get_alt<_Ip>(*this), std::forward<_Args>(__args)...);
|
||||
std::__construct_at(std::addressof(this->__data), in_place_index<_Ip>, std::forward<_Args>(__args)...);
|
||||
this->__index = _Ip;
|
||||
return __res;
|
||||
return __access::__base::__get_alt<_Ip>(*this).__value;
|
||||
}
|
||||
|
||||
protected:
|
||||
template <size_t _Ip, class _Tp, class _Arg>
|
||||
_LIBCPP_HIDE_FROM_ABI void __assign_alt(__alt<_Ip, _Tp>& __a, _Arg&& __arg) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_alt(__alt<_Ip, _Tp>& __a, _Arg&& __arg) {
|
||||
if (this->index() == _Ip) {
|
||||
__a.__value = std::forward<_Arg>(__arg);
|
||||
} else {
|
||||
struct {
|
||||
_LIBCPP_HIDDEN void operator()(true_type) const { __this->__emplace<_Ip>(std::forward<_Arg>(__arg)); }
|
||||
_LIBCPP_HIDDEN void operator()(false_type) const {
|
||||
_LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()(true_type) const {
|
||||
__this->__emplace<_Ip>(std::forward<_Arg>(__arg));
|
||||
}
|
||||
_LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX20 void operator()(false_type) const {
|
||||
__this->__emplace<_Ip>(_Tp(std::forward<_Arg>(__arg)));
|
||||
}
|
||||
__assignment* __this;
|
||||
@ -934,7 +942,7 @@ protected:
|
||||
}
|
||||
|
||||
template <class _That>
|
||||
_LIBCPP_HIDE_FROM_ABI void __generic_assign(_That&& __that) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __generic_assign(_That&& __that) {
|
||||
if (this->valueless_by_exception() && __that.valueless_by_exception()) {
|
||||
// do nothing.
|
||||
} else if (__that.valueless_by_exception()) {
|
||||
@ -954,7 +962,7 @@ protected:
|
||||
template <class _Traits, _Trait = _Traits::__move_assignable_trait>
|
||||
class _LIBCPP_TEMPLATE_VIS __move_assignment;
|
||||
|
||||
# define _LIBCPP_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, move_assignment) \
|
||||
# define _LIBCPP_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, move_assignment_definition) \
|
||||
template <class... _Types> \
|
||||
class _LIBCPP_TEMPLATE_VIS __move_assignment<__traits<_Types...>, move_assignable_trait> \
|
||||
: public __assignment<__traits<_Types...>> { \
|
||||
@ -964,33 +972,36 @@ class _LIBCPP_TEMPLATE_VIS __move_assignment;
|
||||
using __base_type::__base_type; \
|
||||
using __base_type::operator=; \
|
||||
\
|
||||
__move_assignment(const __move_assignment&) = default; \
|
||||
__move_assignment(__move_assignment&&) = default; \
|
||||
~__move_assignment() = default; \
|
||||
__move_assignment& operator=(const __move_assignment&) = default; \
|
||||
move_assignment; \
|
||||
_LIBCPP_HIDE_FROM_ABI __move_assignment(const __move_assignment&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __move_assignment(__move_assignment&&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI ~__move_assignment() = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __move_assignment& operator=(const __move_assignment&) = default; \
|
||||
move_assignment_definition; \
|
||||
}
|
||||
|
||||
_LIBCPP_VARIANT_MOVE_ASSIGNMENT(_Trait::_TriviallyAvailable,
|
||||
__move_assignment& operator=(__move_assignment&& __that) = default);
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_assignment& operator=(
|
||||
__move_assignment&& __that) = default);
|
||||
|
||||
_LIBCPP_VARIANT_MOVE_ASSIGNMENT(
|
||||
_Trait::_Available,
|
||||
_LIBCPP_HIDE_FROM_ABI __move_assignment&
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_assignment&
|
||||
operator=(__move_assignment&& __that) noexcept(
|
||||
__all<(is_nothrow_move_constructible_v<_Types> && is_nothrow_move_assignable_v<_Types>)...>::value) {
|
||||
this->__generic_assign(std::move(__that));
|
||||
return *this;
|
||||
} _LIBCPP_EAT_SEMICOLON);
|
||||
|
||||
_LIBCPP_VARIANT_MOVE_ASSIGNMENT(_Trait::_Unavailable, __move_assignment& operator=(__move_assignment&&) = delete);
|
||||
_LIBCPP_VARIANT_MOVE_ASSIGNMENT(
|
||||
_Trait::_Unavailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __move_assignment& operator=(__move_assignment&&) = delete);
|
||||
|
||||
# undef _LIBCPP_VARIANT_MOVE_ASSIGNMENT
|
||||
|
||||
template <class _Traits, _Trait = _Traits::__copy_assignable_trait>
|
||||
class _LIBCPP_TEMPLATE_VIS __copy_assignment;
|
||||
|
||||
# define _LIBCPP_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, copy_assignment) \
|
||||
# define _LIBCPP_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, copy_assignment_definition) \
|
||||
template <class... _Types> \
|
||||
class _LIBCPP_TEMPLATE_VIS __copy_assignment<__traits<_Types...>, copy_assignable_trait> \
|
||||
: public __move_assignment<__traits<_Types...>> { \
|
||||
@ -1000,23 +1011,28 @@ class _LIBCPP_TEMPLATE_VIS __copy_assignment;
|
||||
using __base_type::__base_type; \
|
||||
using __base_type::operator=; \
|
||||
\
|
||||
__copy_assignment(const __copy_assignment&) = default; \
|
||||
__copy_assignment(__copy_assignment&&) = default; \
|
||||
~__copy_assignment() = default; \
|
||||
__copy_assignment& operator=(__copy_assignment&&) = default; \
|
||||
copy_assignment; \
|
||||
_LIBCPP_HIDE_FROM_ABI __copy_assignment(const __copy_assignment&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __copy_assignment(__copy_assignment&&) = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI ~__copy_assignment() = default; \
|
||||
_LIBCPP_HIDE_FROM_ABI __copy_assignment& operator=(__copy_assignment&&) = default; \
|
||||
copy_assignment_definition; \
|
||||
}
|
||||
|
||||
_LIBCPP_VARIANT_COPY_ASSIGNMENT(_Trait::_TriviallyAvailable,
|
||||
__copy_assignment& operator=(const __copy_assignment& __that) = default);
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_assignment& operator=(
|
||||
const __copy_assignment& __that) = default);
|
||||
|
||||
_LIBCPP_VARIANT_COPY_ASSIGNMENT(
|
||||
_Trait::_Available, _LIBCPP_HIDE_FROM_ABI __copy_assignment& operator=(const __copy_assignment& __that) {
|
||||
_Trait::_Available,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_assignment&
|
||||
operator=(const __copy_assignment& __that) {
|
||||
this->__generic_assign(__that);
|
||||
return *this;
|
||||
} _LIBCPP_EAT_SEMICOLON);
|
||||
|
||||
_LIBCPP_VARIANT_COPY_ASSIGNMENT(_Trait::_Unavailable, __copy_assignment& operator=(const __copy_assignment&) = delete);
|
||||
_LIBCPP_VARIANT_COPY_ASSIGNMENT(_Trait::_Unavailable,
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_assignment& operator=(
|
||||
const __copy_assignment&) = delete);
|
||||
|
||||
# undef _LIBCPP_VARIANT_COPY_ASSIGNMENT
|
||||
|
||||
@ -1032,11 +1048,11 @@ public:
|
||||
_LIBCPP_HIDE_FROM_ABI __impl& operator=(__impl&&) = default;
|
||||
|
||||
template <size_t _Ip, class _Arg>
|
||||
_LIBCPP_HIDE_FROM_ABI void __assign(_Arg&& __arg) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign(_Arg&& __arg) {
|
||||
this->__assign_alt(__access::__base::__get_alt<_Ip>(*this), std::forward<_Arg>(__arg));
|
||||
}
|
||||
|
||||
inline _LIBCPP_HIDE_FROM_ABI void __swap(__impl& __that) {
|
||||
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__impl& __that) {
|
||||
if (this->valueless_by_exception() && __that.valueless_by_exception()) {
|
||||
// do nothing.
|
||||
} else if (this->index() == __that.index()) {
|
||||
@ -1081,7 +1097,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
inline _LIBCPP_HIDE_FROM_ABI bool __move_nothrow() const {
|
||||
constexpr inline _LIBCPP_HIDE_FROM_ABI bool __move_nothrow() const {
|
||||
constexpr bool __results[] = {is_nothrow_move_constructible_v<_Types>...};
|
||||
return this->valueless_by_exception() || __results[this->index()];
|
||||
}
|
||||
@ -1223,7 +1239,7 @@ public:
|
||||
_Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, initializer_list< _Up>&, _Args...>)
|
||||
: __impl_(in_place_index<_Ip>, __il, std::forward<_Args>(__args)...) {}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI ~variant() = default;
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~variant() = default;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr variant& operator=(const variant&) = default;
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr variant& operator=(variant&&) = default;
|
||||
@ -1233,7 +1249,7 @@ public:
|
||||
class _Tp = __variant_detail::__best_match_t<_Arg, _Types...>,
|
||||
size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
|
||||
enable_if_t<is_assignable_v<_Tp&, _Arg> && is_constructible_v<_Tp, _Arg>, int> = 0>
|
||||
_LIBCPP_HIDE_FROM_ABI variant&
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 variant&
|
||||
operator=(_Arg&& __arg) noexcept(is_nothrow_assignable_v<_Tp&, _Arg> && is_nothrow_constructible_v<_Tp, _Arg>) {
|
||||
__impl_.template __assign<_Ip>(std::forward<_Arg>(__arg));
|
||||
return *this;
|
||||
@ -1244,7 +1260,7 @@ public:
|
||||
enable_if_t<(_Ip < sizeof...(_Types)), int> = 0,
|
||||
class _Tp = variant_alternative_t<_Ip, variant<_Types...>>,
|
||||
enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
|
||||
_LIBCPP_HIDE_FROM_ABI _Tp& emplace(_Args&&... __args) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
|
||||
return __impl_.template __emplace<_Ip>(std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
@ -1254,7 +1270,7 @@ public:
|
||||
enable_if_t<(_Ip < sizeof...(_Types)), int> = 0,
|
||||
class _Tp = variant_alternative_t<_Ip, variant<_Types...>>,
|
||||
enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
|
||||
_LIBCPP_HIDE_FROM_ABI _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
|
||||
return __impl_.template __emplace<_Ip>(__il, std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
@ -1262,7 +1278,7 @@ public:
|
||||
class... _Args,
|
||||
size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
|
||||
enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
|
||||
_LIBCPP_HIDE_FROM_ABI _Tp& emplace(_Args&&... __args) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) {
|
||||
return __impl_.template __emplace<_Ip>(std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
@ -1271,7 +1287,7 @@ public:
|
||||
class... _Args,
|
||||
size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
|
||||
enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
|
||||
_LIBCPP_HIDE_FROM_ABI _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) {
|
||||
return __impl_.template __emplace<_Ip>(__il, std::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
@ -1285,7 +1301,7 @@ public:
|
||||
enable_if_t< __all<(__dependent_type<is_move_constructible<_Types>, _Dummy>::value &&
|
||||
__dependent_type<is_swappable<_Types>, _Dummy>::value)...>::value,
|
||||
int> = 0>
|
||||
_LIBCPP_HIDE_FROM_ABI void swap(variant& __that) noexcept(
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(variant& __that) noexcept(
|
||||
__all<(is_nothrow_move_constructible_v<_Types> && is_nothrow_swappable_v<_Types>)...>::value) {
|
||||
__impl_.__swap(__that.__impl_);
|
||||
}
|
||||
@ -1568,7 +1584,7 @@ visit(_Visitor&& __visitor, _Vs&&... __vs) {
|
||||
# endif
|
||||
|
||||
template <class... _Types>
|
||||
_LIBCPP_HIDE_FROM_ABI auto
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 auto
|
||||
swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs)))
|
||||
-> decltype(__lhs.swap(__rhs)) {
|
||||
return __lhs.swap(__rhs);
|
||||
|
@ -33,17 +33,17 @@ struct Dummy {
|
||||
|
||||
struct ThrowsCtorT {
|
||||
ThrowsCtorT(int) noexcept(false) {}
|
||||
ThrowsCtorT &operator=(int) noexcept { return *this; }
|
||||
ThrowsCtorT& operator=(int) noexcept { return *this; }
|
||||
};
|
||||
|
||||
struct ThrowsAssignT {
|
||||
ThrowsAssignT(int) noexcept {}
|
||||
ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
|
||||
ThrowsAssignT& operator=(int) noexcept(false) { return *this; }
|
||||
};
|
||||
|
||||
struct NoThrowT {
|
||||
NoThrowT(int) noexcept {}
|
||||
NoThrowT &operator=(int) noexcept { return *this; }
|
||||
NoThrowT& operator=(int) noexcept { return *this; }
|
||||
};
|
||||
|
||||
} // namespace MetaHelpers
|
||||
@ -55,7 +55,7 @@ struct ThrowsCtorT {
|
||||
int value;
|
||||
ThrowsCtorT() : value(0) {}
|
||||
ThrowsCtorT(int) noexcept(false) { throw 42; }
|
||||
ThrowsCtorT &operator=(int v) noexcept {
|
||||
ThrowsCtorT& operator=(int v) noexcept {
|
||||
value = v;
|
||||
return *this;
|
||||
}
|
||||
@ -64,9 +64,12 @@ struct ThrowsCtorT {
|
||||
struct MoveCrashes {
|
||||
int value;
|
||||
MoveCrashes(int v = 0) noexcept : value{v} {}
|
||||
MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
|
||||
MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
|
||||
MoveCrashes &operator=(int v) noexcept {
|
||||
MoveCrashes(MoveCrashes&&) noexcept { assert(false); }
|
||||
MoveCrashes& operator=(MoveCrashes&&) noexcept {
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
MoveCrashes& operator=(int v) noexcept {
|
||||
value = v;
|
||||
return *this;
|
||||
}
|
||||
@ -76,8 +79,8 @@ struct ThrowsCtorTandMove {
|
||||
int value;
|
||||
ThrowsCtorTandMove() : value(0) {}
|
||||
ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
|
||||
ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
|
||||
ThrowsCtorTandMove &operator=(int v) noexcept {
|
||||
ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); }
|
||||
ThrowsCtorTandMove& operator=(int v) noexcept {
|
||||
value = v;
|
||||
return *this;
|
||||
}
|
||||
@ -87,14 +90,14 @@ struct ThrowsAssignT {
|
||||
int value;
|
||||
ThrowsAssignT() : value(0) {}
|
||||
ThrowsAssignT(int v) noexcept : value(v) {}
|
||||
ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
|
||||
ThrowsAssignT& operator=(int) noexcept(false) { throw 42; }
|
||||
};
|
||||
|
||||
struct NoThrowT {
|
||||
int value;
|
||||
NoThrowT() : value(0) {}
|
||||
NoThrowT(int v) noexcept : value(v) {}
|
||||
NoThrowT &operator=(int v) noexcept {
|
||||
NoThrowT& operator=(int v) noexcept {
|
||||
value = v;
|
||||
return *this;
|
||||
}
|
||||
@ -103,7 +106,7 @@ struct NoThrowT {
|
||||
#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
|
||||
} // namespace RuntimeHelpers
|
||||
|
||||
void test_T_assignment_noexcept() {
|
||||
constexpr void test_T_assignment_noexcept() {
|
||||
using namespace MetaHelpers;
|
||||
{
|
||||
using V = std::variant<Dummy, NoThrowT>;
|
||||
@ -119,17 +122,17 @@ void test_T_assignment_noexcept() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_T_assignment_sfinae() {
|
||||
constexpr void test_T_assignment_sfinae() {
|
||||
{
|
||||
using V = std::variant<long, long long>;
|
||||
static_assert(!std::is_assignable<V, int>::value, "ambiguous");
|
||||
}
|
||||
{
|
||||
using V = std::variant<std::string, std::string>;
|
||||
static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
|
||||
static_assert(!std::is_assignable<V, const char*>::value, "ambiguous");
|
||||
}
|
||||
{
|
||||
using V = std::variant<std::string, void *>;
|
||||
using V = std::variant<std::string, void*>;
|
||||
static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
|
||||
}
|
||||
{
|
||||
@ -138,8 +141,7 @@ void test_T_assignment_sfinae() {
|
||||
}
|
||||
{
|
||||
using V = std::variant<std::unique_ptr<int>, bool>;
|
||||
static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
|
||||
"no explicit bool in operator=");
|
||||
static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, "no explicit bool in operator=");
|
||||
struct X {
|
||||
operator void*();
|
||||
};
|
||||
@ -152,12 +154,11 @@ void test_T_assignment_sfinae() {
|
||||
operator X();
|
||||
};
|
||||
using V = std::variant<X>;
|
||||
static_assert(std::is_assignable<V, Y>::value,
|
||||
"regression on user-defined conversions in operator=");
|
||||
static_assert(std::is_assignable<V, Y>::value, "regression on user-defined conversions in operator=");
|
||||
}
|
||||
}
|
||||
|
||||
void test_T_assignment_basic() {
|
||||
TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() {
|
||||
{
|
||||
std::variant<int> v(43);
|
||||
v = 42;
|
||||
@ -184,19 +185,146 @@ void test_T_assignment_basic() {
|
||||
}
|
||||
{
|
||||
std::variant<std::string, bool> v = true;
|
||||
v = "bar";
|
||||
v = "bar";
|
||||
assert(v.index() == 0);
|
||||
assert(std::get<0>(v) == "bar");
|
||||
}
|
||||
}
|
||||
|
||||
void test_T_assignment_basic_no_constexpr() {
|
||||
std::variant<bool, std::unique_ptr<int>> v;
|
||||
v = nullptr;
|
||||
assert(v.index() == 1);
|
||||
assert(std::get<1>(v) == nullptr);
|
||||
}
|
||||
|
||||
struct TraceStat {
|
||||
int construct = 0;
|
||||
int copy_construct = 0;
|
||||
int copy_assign = 0;
|
||||
int move_construct = 0;
|
||||
int move_assign = 0;
|
||||
int T_copy_assign = 0;
|
||||
int T_move_assign = 0;
|
||||
int destroy = 0;
|
||||
};
|
||||
|
||||
template <bool CtorNoexcept, bool MoveCtorNoexcept>
|
||||
struct Trace {
|
||||
struct T {};
|
||||
|
||||
constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; }
|
||||
constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {}
|
||||
constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; }
|
||||
constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; }
|
||||
constexpr Trace& operator=(const Trace&) {
|
||||
++stat->copy_assign;
|
||||
return *this;
|
||||
}
|
||||
constexpr Trace& operator=(Trace&&) noexcept {
|
||||
++stat->move_assign;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Trace& operator=(const T&) {
|
||||
++stat->T_copy_assign;
|
||||
return *this;
|
||||
}
|
||||
constexpr Trace& operator=(T&&) noexcept {
|
||||
++stat->T_move_assign;
|
||||
return *this;
|
||||
}
|
||||
TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; }
|
||||
|
||||
TraceStat* stat;
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() {
|
||||
{
|
||||
std::variant<bool, std::unique_ptr<int>> v;
|
||||
v = nullptr;
|
||||
assert(v.index() == 1);
|
||||
assert(std::get<1>(v) == nullptr);
|
||||
using V = std::variant<int, Trace<false, false>>;
|
||||
TraceStat stat;
|
||||
V v{1};
|
||||
v = &stat;
|
||||
assert(stat.construct == 1);
|
||||
assert(stat.copy_construct == 0);
|
||||
assert(stat.move_construct == 0);
|
||||
assert(stat.copy_assign == 0);
|
||||
assert(stat.move_assign == 0);
|
||||
assert(stat.destroy == 0);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, Trace<false, true>>;
|
||||
TraceStat stat;
|
||||
V v{1};
|
||||
v = &stat;
|
||||
assert(stat.construct == 1);
|
||||
assert(stat.copy_construct == 0);
|
||||
assert(stat.move_construct == 1);
|
||||
assert(stat.copy_assign == 0);
|
||||
assert(stat.move_assign == 0);
|
||||
assert(stat.destroy == 1);
|
||||
}
|
||||
|
||||
{
|
||||
using V = std::variant<int, Trace<true, false>>;
|
||||
TraceStat stat;
|
||||
V v{1};
|
||||
v = &stat;
|
||||
assert(stat.construct == 1);
|
||||
assert(stat.copy_construct == 0);
|
||||
assert(stat.move_construct == 0);
|
||||
assert(stat.copy_assign == 0);
|
||||
assert(stat.move_assign == 0);
|
||||
assert(stat.destroy == 0);
|
||||
}
|
||||
|
||||
{
|
||||
using V = std::variant<int, Trace<true, true>>;
|
||||
TraceStat stat;
|
||||
V v{1};
|
||||
v = &stat;
|
||||
assert(stat.construct == 1);
|
||||
assert(stat.copy_construct == 0);
|
||||
assert(stat.move_construct == 0);
|
||||
assert(stat.copy_assign == 0);
|
||||
assert(stat.move_assign == 0);
|
||||
assert(stat.destroy == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void test_T_assignment_performs_construction() {
|
||||
TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() {
|
||||
{
|
||||
using V = std::variant<int, Trace<false, false>>;
|
||||
TraceStat stat;
|
||||
V v{&stat};
|
||||
v = Trace<false, false>::T{};
|
||||
assert(stat.construct == 1);
|
||||
assert(stat.copy_construct == 0);
|
||||
assert(stat.move_construct == 0);
|
||||
assert(stat.copy_assign == 0);
|
||||
assert(stat.move_assign == 0);
|
||||
assert(stat.T_copy_assign == 0);
|
||||
assert(stat.T_move_assign == 1);
|
||||
assert(stat.destroy == 0);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, Trace<false, false>>;
|
||||
TraceStat stat;
|
||||
V v{&stat};
|
||||
Trace<false, false>::T t;
|
||||
v = t;
|
||||
assert(stat.construct == 1);
|
||||
assert(stat.copy_construct == 0);
|
||||
assert(stat.move_construct == 0);
|
||||
assert(stat.copy_assign == 0);
|
||||
assert(stat.move_assign == 0);
|
||||
assert(stat.T_copy_assign == 1);
|
||||
assert(stat.T_move_assign == 0);
|
||||
assert(stat.destroy == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void test_T_assignment_performs_construction_throw() {
|
||||
using namespace RuntimeHelpers;
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
{
|
||||
@ -220,7 +348,7 @@ void test_T_assignment_performs_construction() {
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
void test_T_assignment_performs_assignment() {
|
||||
void test_T_assignment_performs_assignment_throw() {
|
||||
using namespace RuntimeHelpers;
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
{
|
||||
@ -262,7 +390,7 @@ void test_T_assignment_performs_assignment() {
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
void test_T_assignment_vector_bool() {
|
||||
TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() {
|
||||
std::vector<bool> vec = {true};
|
||||
std::variant<bool, int> v;
|
||||
v = vec[0];
|
||||
@ -270,7 +398,13 @@ void test_T_assignment_vector_bool() {
|
||||
assert(std::get<0>(v) == true);
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
void non_constexpr_test() {
|
||||
test_T_assignment_basic_no_constexpr();
|
||||
test_T_assignment_performs_construction_throw();
|
||||
test_T_assignment_performs_assignment_throw();
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test_T_assignment_basic();
|
||||
test_T_assignment_performs_construction();
|
||||
test_T_assignment_performs_assignment();
|
||||
@ -278,5 +412,15 @@ int main(int, char**) {
|
||||
test_T_assignment_sfinae();
|
||||
test_T_assignment_vector_bool();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
non_constexpr_test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,88 +22,108 @@
|
||||
#include "test_macros.h"
|
||||
|
||||
struct NoCopy {
|
||||
NoCopy(const NoCopy &) = delete;
|
||||
NoCopy &operator=(const NoCopy &) = default;
|
||||
NoCopy(const NoCopy&) = delete;
|
||||
NoCopy& operator=(const NoCopy&) = default;
|
||||
};
|
||||
|
||||
struct CopyOnly {
|
||||
CopyOnly(const CopyOnly &) = default;
|
||||
CopyOnly(CopyOnly &&) = delete;
|
||||
CopyOnly &operator=(const CopyOnly &) = default;
|
||||
CopyOnly &operator=(CopyOnly &&) = delete;
|
||||
CopyOnly(const CopyOnly&) = default;
|
||||
CopyOnly(CopyOnly&&) = delete;
|
||||
CopyOnly& operator=(const CopyOnly&) = default;
|
||||
CopyOnly& operator=(CopyOnly&&) = delete;
|
||||
};
|
||||
|
||||
struct MoveOnly {
|
||||
MoveOnly(const MoveOnly &) = delete;
|
||||
MoveOnly(MoveOnly &&) = default;
|
||||
MoveOnly &operator=(const MoveOnly &) = default;
|
||||
MoveOnly(const MoveOnly&) = delete;
|
||||
MoveOnly(MoveOnly&&) = default;
|
||||
MoveOnly& operator=(const MoveOnly&) = default;
|
||||
};
|
||||
|
||||
struct MoveOnlyNT {
|
||||
MoveOnlyNT(const MoveOnlyNT &) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT &&) {}
|
||||
MoveOnlyNT &operator=(const MoveOnlyNT &) = default;
|
||||
MoveOnlyNT(const MoveOnlyNT&) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT&&) {}
|
||||
MoveOnlyNT& operator=(const MoveOnlyNT&) = default;
|
||||
};
|
||||
|
||||
struct CopyAssign {
|
||||
static int alive;
|
||||
static int copy_construct;
|
||||
static int copy_assign;
|
||||
static int move_construct;
|
||||
static int move_assign;
|
||||
static void reset() {
|
||||
copy_construct = copy_assign = move_construct = move_assign = alive = 0;
|
||||
constexpr CopyAssign(int v, int* alv, int* cpy_ctr, int* cpy_assi, int* move_ctr, int* move_assi)
|
||||
: value(v),
|
||||
alive(alv),
|
||||
copy_construct(cpy_ctr),
|
||||
copy_assign(cpy_assi),
|
||||
move_construct(move_ctr),
|
||||
move_assign(move_assi) {
|
||||
++*alive;
|
||||
}
|
||||
CopyAssign(int v) : value(v) { ++alive; }
|
||||
CopyAssign(const CopyAssign &o) : value(o.value) {
|
||||
++alive;
|
||||
++copy_construct;
|
||||
constexpr CopyAssign(const CopyAssign& o)
|
||||
: value(o.value),
|
||||
alive(o.alive),
|
||||
copy_construct(o.copy_construct),
|
||||
copy_assign(o.copy_assign),
|
||||
move_construct(o.move_construct),
|
||||
move_assign(o.move_assign) {
|
||||
++*alive;
|
||||
++*copy_construct;
|
||||
}
|
||||
CopyAssign(CopyAssign &&o) noexcept : value(o.value) {
|
||||
constexpr CopyAssign(CopyAssign&& o) noexcept
|
||||
: value(o.value),
|
||||
alive(o.alive),
|
||||
copy_construct(o.copy_construct),
|
||||
copy_assign(o.copy_assign),
|
||||
move_construct(o.move_construct),
|
||||
move_assign(o.move_assign) {
|
||||
o.value = -1;
|
||||
++alive;
|
||||
++move_construct;
|
||||
++*alive;
|
||||
++*move_construct;
|
||||
}
|
||||
CopyAssign &operator=(const CopyAssign &o) {
|
||||
value = o.value;
|
||||
++copy_assign;
|
||||
constexpr CopyAssign& operator=(const CopyAssign& o) {
|
||||
value = o.value;
|
||||
alive = o.alive;
|
||||
copy_construct = o.copy_construct;
|
||||
copy_assign = o.copy_assign;
|
||||
move_construct = o.move_construct;
|
||||
move_assign = o.move_assign;
|
||||
++*copy_assign;
|
||||
return *this;
|
||||
}
|
||||
CopyAssign &operator=(CopyAssign &&o) noexcept {
|
||||
value = o.value;
|
||||
o.value = -1;
|
||||
++move_assign;
|
||||
constexpr CopyAssign& operator=(CopyAssign&& o) noexcept {
|
||||
value = o.value;
|
||||
alive = o.alive;
|
||||
copy_construct = o.copy_construct;
|
||||
copy_assign = o.copy_assign;
|
||||
move_construct = o.move_construct;
|
||||
move_assign = o.move_assign;
|
||||
o.value = -1;
|
||||
++*move_assign;
|
||||
return *this;
|
||||
}
|
||||
~CopyAssign() { --alive; }
|
||||
TEST_CONSTEXPR_CXX20 ~CopyAssign() { --*alive; }
|
||||
int value;
|
||||
int* alive;
|
||||
int* copy_construct;
|
||||
int* copy_assign;
|
||||
int* move_construct;
|
||||
int* move_assign;
|
||||
};
|
||||
|
||||
int CopyAssign::alive = 0;
|
||||
int CopyAssign::copy_construct = 0;
|
||||
int CopyAssign::copy_assign = 0;
|
||||
int CopyAssign::move_construct = 0;
|
||||
int CopyAssign::move_assign = 0;
|
||||
|
||||
struct CopyMaybeThrows {
|
||||
CopyMaybeThrows(const CopyMaybeThrows &);
|
||||
CopyMaybeThrows &operator=(const CopyMaybeThrows &);
|
||||
CopyMaybeThrows(const CopyMaybeThrows&);
|
||||
CopyMaybeThrows& operator=(const CopyMaybeThrows&);
|
||||
};
|
||||
struct CopyDoesThrow {
|
||||
CopyDoesThrow(const CopyDoesThrow &) noexcept(false);
|
||||
CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false);
|
||||
CopyDoesThrow(const CopyDoesThrow&) noexcept(false);
|
||||
CopyDoesThrow& operator=(const CopyDoesThrow&) noexcept(false);
|
||||
};
|
||||
|
||||
|
||||
struct NTCopyAssign {
|
||||
constexpr NTCopyAssign(int v) : value(v) {}
|
||||
NTCopyAssign(const NTCopyAssign &) = default;
|
||||
NTCopyAssign(NTCopyAssign &&) = default;
|
||||
NTCopyAssign &operator=(const NTCopyAssign &that) {
|
||||
NTCopyAssign(const NTCopyAssign&) = default;
|
||||
NTCopyAssign(NTCopyAssign&&) = default;
|
||||
NTCopyAssign& operator=(const NTCopyAssign& that) {
|
||||
value = that.value;
|
||||
return *this;
|
||||
};
|
||||
NTCopyAssign &operator=(NTCopyAssign &&) = delete;
|
||||
NTCopyAssign& operator=(NTCopyAssign&&) = delete;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -112,10 +132,10 @@ static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");
|
||||
|
||||
struct TCopyAssign {
|
||||
constexpr TCopyAssign(int v) : value(v) {}
|
||||
TCopyAssign(const TCopyAssign &) = default;
|
||||
TCopyAssign(TCopyAssign &&) = default;
|
||||
TCopyAssign &operator=(const TCopyAssign &) = default;
|
||||
TCopyAssign &operator=(TCopyAssign &&) = delete;
|
||||
TCopyAssign(const TCopyAssign&) = default;
|
||||
TCopyAssign(TCopyAssign&&) = default;
|
||||
TCopyAssign& operator=(const TCopyAssign&) = default;
|
||||
TCopyAssign& operator=(TCopyAssign&&) = delete;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -123,11 +143,11 @@ static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");
|
||||
|
||||
struct TCopyAssignNTMoveAssign {
|
||||
constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
|
||||
TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default;
|
||||
TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default;
|
||||
TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default;
|
||||
TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) {
|
||||
value = that.value;
|
||||
TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign&) = default;
|
||||
TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign&&) = default;
|
||||
TCopyAssignNTMoveAssign& operator=(const TCopyAssignNTMoveAssign&) = default;
|
||||
TCopyAssignNTMoveAssign& operator=(TCopyAssignNTMoveAssign&& that) {
|
||||
value = that.value;
|
||||
that.value = -1;
|
||||
return *this;
|
||||
}
|
||||
@ -139,17 +159,20 @@ static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
struct CopyThrows {
|
||||
CopyThrows() = default;
|
||||
CopyThrows(const CopyThrows &) { throw 42; }
|
||||
CopyThrows &operator=(const CopyThrows &) { throw 42; }
|
||||
CopyThrows(const CopyThrows&) { throw 42; }
|
||||
CopyThrows& operator=(const CopyThrows&) { throw 42; }
|
||||
};
|
||||
|
||||
struct CopyCannotThrow {
|
||||
static int alive;
|
||||
CopyCannotThrow() { ++alive; }
|
||||
CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; }
|
||||
CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); }
|
||||
CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default;
|
||||
CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; }
|
||||
CopyCannotThrow(const CopyCannotThrow&) noexcept { ++alive; }
|
||||
CopyCannotThrow(CopyCannotThrow&&) noexcept { assert(false); }
|
||||
CopyCannotThrow& operator=(const CopyCannotThrow&) noexcept = default;
|
||||
CopyCannotThrow& operator=(CopyCannotThrow&&) noexcept {
|
||||
assert(false);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
int CopyCannotThrow::alive = 0;
|
||||
@ -157,10 +180,10 @@ int CopyCannotThrow::alive = 0;
|
||||
struct MoveThrows {
|
||||
static int alive;
|
||||
MoveThrows() { ++alive; }
|
||||
MoveThrows(const MoveThrows &) { ++alive; }
|
||||
MoveThrows(MoveThrows &&) { throw 42; }
|
||||
MoveThrows &operator=(const MoveThrows &) { return *this; }
|
||||
MoveThrows &operator=(MoveThrows &&) { throw 42; }
|
||||
MoveThrows(const MoveThrows&) { ++alive; }
|
||||
MoveThrows(MoveThrows&&) { throw 42; }
|
||||
MoveThrows& operator=(const MoveThrows&) { return *this; }
|
||||
MoveThrows& operator=(MoveThrows&&) { throw 42; }
|
||||
~MoveThrows() { --alive; }
|
||||
};
|
||||
|
||||
@ -169,20 +192,21 @@ int MoveThrows::alive = 0;
|
||||
struct MakeEmptyT {
|
||||
static int alive;
|
||||
MakeEmptyT() { ++alive; }
|
||||
MakeEmptyT(const MakeEmptyT &) {
|
||||
MakeEmptyT(const MakeEmptyT&) {
|
||||
++alive;
|
||||
// Don't throw from the copy constructor since variant's assignment
|
||||
// operator performs a copy before committing to the assignment.
|
||||
}
|
||||
MakeEmptyT(MakeEmptyT &&) { throw 42; }
|
||||
MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
|
||||
MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
|
||||
MakeEmptyT(MakeEmptyT&&) { throw 42; }
|
||||
MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
|
||||
MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
|
||||
~MakeEmptyT() { --alive; }
|
||||
};
|
||||
|
||||
int MakeEmptyT::alive = 0;
|
||||
|
||||
template <class Variant> void makeEmpty(Variant &v) {
|
||||
template <class Variant>
|
||||
void makeEmpty(Variant& v) {
|
||||
Variant v2(std::in_place_type<MakeEmptyT>);
|
||||
try {
|
||||
v = std::move(v2);
|
||||
@ -193,7 +217,7 @@ template <class Variant> void makeEmpty(Variant &v) {
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
void test_copy_assignment_not_noexcept() {
|
||||
constexpr void test_copy_assignment_not_noexcept() {
|
||||
{
|
||||
using V = std::variant<CopyMaybeThrows>;
|
||||
static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
|
||||
@ -204,7 +228,7 @@ void test_copy_assignment_not_noexcept() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_copy_assignment_sfinae() {
|
||||
constexpr void test_copy_assignment_sfinae() {
|
||||
{
|
||||
using V = std::variant<int, long>;
|
||||
static_assert(std::is_copy_assignable<V>::value, "");
|
||||
@ -259,7 +283,7 @@ void test_copy_assignment_empty_empty() {
|
||||
makeEmpty(v1);
|
||||
V v2(std::in_place_index<0>);
|
||||
makeEmpty(v2);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
@ -275,7 +299,7 @@ void test_copy_assignment_non_empty_empty() {
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>);
|
||||
makeEmpty(v2);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
@ -285,7 +309,7 @@ void test_copy_assignment_non_empty_empty() {
|
||||
V v1(std::in_place_index<2>, "hello");
|
||||
V v2(std::in_place_index<0>);
|
||||
makeEmpty(v2);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
@ -301,7 +325,7 @@ void test_copy_assignment_empty_non_empty() {
|
||||
V v1(std::in_place_index<0>);
|
||||
makeEmpty(v1);
|
||||
V v2(std::in_place_index<0>, 42);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 0);
|
||||
assert(std::get<0>(v1) == 42);
|
||||
@ -311,7 +335,7 @@ void test_copy_assignment_empty_non_empty() {
|
||||
V v1(std::in_place_index<0>);
|
||||
makeEmpty(v1);
|
||||
V v2(std::in_place_type<std::string>, "hello");
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 2);
|
||||
assert(std::get<2>(v1) == "hello");
|
||||
@ -319,14 +343,18 @@ void test_copy_assignment_empty_non_empty() {
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
template <typename T> struct Result { std::size_t index; T value; };
|
||||
template <typename T>
|
||||
struct Result {
|
||||
std::size_t index;
|
||||
T value;
|
||||
};
|
||||
|
||||
void test_copy_assignment_same_index() {
|
||||
TEST_CONSTEXPR_CXX20 void test_copy_assignment_same_index() {
|
||||
{
|
||||
using V = std::variant<int>;
|
||||
V v1(43);
|
||||
V v2(42);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 0);
|
||||
assert(std::get<0>(v1) == 42);
|
||||
@ -335,40 +363,28 @@ void test_copy_assignment_same_index() {
|
||||
using V = std::variant<int, long, unsigned>;
|
||||
V v1(43l);
|
||||
V v2(42l);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1) == 42);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, CopyAssign, unsigned>;
|
||||
V v1(std::in_place_type<CopyAssign>, 43);
|
||||
V v2(std::in_place_type<CopyAssign>, 42);
|
||||
CopyAssign::reset();
|
||||
V &vref = (v1 = v2);
|
||||
using V = std::variant<int, CopyAssign, unsigned>;
|
||||
int alive = 0;
|
||||
int copy_construct = 0;
|
||||
int copy_assign = 0;
|
||||
int move_construct = 0;
|
||||
int move_assign = 0;
|
||||
V v1(std::in_place_type<CopyAssign>, 43, &alive, ©_construct, ©_assign, &move_construct, &move_assign);
|
||||
V v2(std::in_place_type<CopyAssign>, 42, &alive, ©_construct, ©_assign, &move_construct, &move_assign);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1).value == 42);
|
||||
assert(CopyAssign::copy_construct == 0);
|
||||
assert(CopyAssign::move_construct == 0);
|
||||
assert(CopyAssign::copy_assign == 1);
|
||||
assert(copy_construct == 0);
|
||||
assert(move_construct == 0);
|
||||
assert(copy_assign == 1);
|
||||
}
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
using MET = MakeEmptyT;
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<MET>);
|
||||
MET &mref = std::get<1>(v1);
|
||||
V v2(std::in_place_type<MET>);
|
||||
try {
|
||||
v1 = v2;
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
assert(v1.index() == 1);
|
||||
assert(&std::get<1>(v1) == &mref);
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
{
|
||||
@ -429,34 +445,88 @@ void test_copy_assignment_same_index() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_copy_assignment_different_index() {
|
||||
TEST_CONSTEXPR_CXX20 void test_copy_assignment_different_index() {
|
||||
{
|
||||
using V = std::variant<int, long, unsigned>;
|
||||
V v1(43);
|
||||
V v2(42l);
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1) == 42);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, CopyAssign, unsigned>;
|
||||
CopyAssign::reset();
|
||||
using V = std::variant<int, CopyAssign, unsigned>;
|
||||
int alive = 0;
|
||||
int copy_construct = 0;
|
||||
int copy_assign = 0;
|
||||
int move_construct = 0;
|
||||
int move_assign = 0;
|
||||
V v1(std::in_place_type<unsigned>, 43u);
|
||||
V v2(std::in_place_type<CopyAssign>, 42);
|
||||
assert(CopyAssign::copy_construct == 0);
|
||||
assert(CopyAssign::move_construct == 0);
|
||||
assert(CopyAssign::alive == 1);
|
||||
V &vref = (v1 = v2);
|
||||
V v2(std::in_place_type<CopyAssign>, 42, &alive, ©_construct, ©_assign, &move_construct, &move_assign);
|
||||
assert(copy_construct == 0);
|
||||
assert(move_construct == 0);
|
||||
assert(alive == 1);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1).value == 42);
|
||||
assert(CopyAssign::alive == 2);
|
||||
assert(CopyAssign::copy_construct == 1);
|
||||
assert(CopyAssign::move_construct == 1);
|
||||
assert(CopyAssign::copy_assign == 0);
|
||||
assert(alive == 2);
|
||||
assert(copy_construct == 1);
|
||||
assert(move_construct == 1);
|
||||
assert(copy_assign == 0);
|
||||
}
|
||||
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
{
|
||||
struct {
|
||||
constexpr Result<long> operator()() const {
|
||||
using V = std::variant<int, long, unsigned>;
|
||||
V v(43);
|
||||
V v2(42l);
|
||||
v = v2;
|
||||
return {v.index(), std::get<1>(v)};
|
||||
}
|
||||
} test;
|
||||
constexpr auto result = test();
|
||||
static_assert(result.index == 1, "");
|
||||
static_assert(result.value == 42l, "");
|
||||
}
|
||||
{
|
||||
struct {
|
||||
constexpr Result<int> operator()() const {
|
||||
using V = std::variant<int, TCopyAssign, unsigned>;
|
||||
V v(std::in_place_type<unsigned>, 43u);
|
||||
V v2(std::in_place_type<TCopyAssign>, 42);
|
||||
v = v2;
|
||||
return {v.index(), std::get<1>(v).value};
|
||||
}
|
||||
} test;
|
||||
constexpr auto result = test();
|
||||
static_assert(result.index == 1, "");
|
||||
static_assert(result.value == 42, "");
|
||||
}
|
||||
}
|
||||
|
||||
void test_assignment_throw() {
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
using MET = MakeEmptyT;
|
||||
// same index
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<MET>);
|
||||
MET& mref = std::get<1>(v1);
|
||||
V v2(std::in_place_type<MET>);
|
||||
try {
|
||||
v1 = v2;
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
assert(v1.index() == 1);
|
||||
assert(&std::get<1>(v1) == &mref);
|
||||
}
|
||||
|
||||
// difference indices
|
||||
{
|
||||
using V = std::variant<int, CopyThrows, std::string>;
|
||||
V v1(std::in_place_type<std::string>, "hello");
|
||||
@ -496,7 +566,7 @@ void test_copy_assignment_different_index() {
|
||||
using V = std::variant<int, CopyThrows, std::string>;
|
||||
V v1(std::in_place_type<CopyThrows>);
|
||||
V v2(std::in_place_type<std::string>, "hello");
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 2);
|
||||
assert(std::get<2>(v1) == "hello");
|
||||
@ -507,7 +577,7 @@ void test_copy_assignment_different_index() {
|
||||
using V = std::variant<int, MoveThrows, std::string>;
|
||||
V v1(std::in_place_type<MoveThrows>);
|
||||
V v2(std::in_place_type<std::string>, "hello");
|
||||
V &vref = (v1 = v2);
|
||||
V& vref = (v1 = v2);
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 2);
|
||||
assert(std::get<2>(v1) == "hello");
|
||||
@ -515,69 +585,83 @@ void test_copy_assignment_different_index() {
|
||||
assert(std::get<2>(v2) == "hello");
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
{
|
||||
struct {
|
||||
constexpr Result<long> operator()() const {
|
||||
using V = std::variant<int, long, unsigned>;
|
||||
V v(43);
|
||||
V v2(42l);
|
||||
v = v2;
|
||||
return {v.index(), std::get<1>(v)};
|
||||
}
|
||||
} test;
|
||||
constexpr auto result = test();
|
||||
static_assert(result.index == 1, "");
|
||||
static_assert(result.value == 42l, "");
|
||||
}
|
||||
{
|
||||
struct {
|
||||
constexpr Result<int> operator()() const {
|
||||
using V = std::variant<int, TCopyAssign, unsigned>;
|
||||
V v(std::in_place_type<unsigned>, 43u);
|
||||
V v2(std::in_place_type<TCopyAssign>, 42);
|
||||
v = v2;
|
||||
return {v.index(), std::get<1>(v).value};
|
||||
}
|
||||
} test;
|
||||
constexpr auto result = test();
|
||||
static_assert(result.index == 1, "");
|
||||
static_assert(result.value == 42, "");
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t NewIdx, class ValueType>
|
||||
constexpr bool test_constexpr_assign_imp(
|
||||
std::variant<long, void*, int>&& v, ValueType&& new_value)
|
||||
{
|
||||
const std::variant<long, void*, int> cp(
|
||||
std::forward<ValueType>(new_value));
|
||||
template <std::size_t NewIdx, class T, class ValueType>
|
||||
constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
|
||||
using Variant = std::decay_t<T>;
|
||||
const Variant cp(std::forward<ValueType>(new_value));
|
||||
v = cp;
|
||||
return v.index() == NewIdx &&
|
||||
std::get<NewIdx>(v) == std::get<NewIdx>(cp);
|
||||
assert(v.index() == NewIdx);
|
||||
assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));
|
||||
}
|
||||
|
||||
void test_constexpr_copy_assignment() {
|
||||
constexpr void test_constexpr_copy_assignment_trivial() {
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
using V = std::variant<long, void*, int>;
|
||||
static_assert(std::is_trivially_copyable<V>::value, "");
|
||||
static_assert(std::is_trivially_copy_assignable<V>::value, "");
|
||||
static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), "");
|
||||
static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), "");
|
||||
static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), "");
|
||||
static_assert(test_constexpr_assign_imp<2>(V(42l), 101), "");
|
||||
test_constexpr_assign_imp<0>(V(42l), 101l);
|
||||
test_constexpr_assign_imp<0>(V(nullptr), 101l);
|
||||
test_constexpr_assign_imp<1>(V(42l), nullptr);
|
||||
test_constexpr_assign_imp<2>(V(42l), 101);
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
struct NonTrivialCopyAssign {
|
||||
int i = 0;
|
||||
constexpr NonTrivialCopyAssign(int ii) : i(ii) {}
|
||||
constexpr NonTrivialCopyAssign(const NonTrivialCopyAssign& other) : i(other.i) {}
|
||||
constexpr NonTrivialCopyAssign& operator=(const NonTrivialCopyAssign& o) {
|
||||
i = o.i;
|
||||
return *this;
|
||||
}
|
||||
TEST_CONSTEXPR_CXX20 ~NonTrivialCopyAssign() = default;
|
||||
friend constexpr bool operator==(const NonTrivialCopyAssign& x, const NonTrivialCopyAssign& y) { return x.i == y.i; }
|
||||
};
|
||||
|
||||
constexpr void test_constexpr_copy_assignment_non_trivial() {
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
using V = std::variant<long, void*, NonTrivialCopyAssign>;
|
||||
static_assert(!std::is_trivially_copyable<V>::value, "");
|
||||
static_assert(!std::is_trivially_copy_assignable<V>::value, "");
|
||||
test_constexpr_assign_imp<0>(V(42l), 101l);
|
||||
test_constexpr_assign_imp<0>(V(nullptr), 101l);
|
||||
test_constexpr_assign_imp<1>(V(42l), nullptr);
|
||||
test_constexpr_assign_imp<2>(V(42l), NonTrivialCopyAssign(5));
|
||||
test_constexpr_assign_imp<2>(V(NonTrivialCopyAssign(3)), NonTrivialCopyAssign(5));
|
||||
}
|
||||
|
||||
void non_constexpr_test() {
|
||||
test_copy_assignment_empty_empty();
|
||||
test_copy_assignment_non_empty_empty();
|
||||
test_copy_assignment_empty_non_empty();
|
||||
test_copy_assignment_same_index();
|
||||
test_copy_assignment_different_index();
|
||||
test_assignment_throw();
|
||||
}
|
||||
|
||||
constexpr bool cxx17_constexpr_test() {
|
||||
test_copy_assignment_sfinae();
|
||||
test_copy_assignment_not_noexcept();
|
||||
test_constexpr_copy_assignment();
|
||||
test_constexpr_copy_assignment_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
|
||||
test_copy_assignment_same_index();
|
||||
test_copy_assignment_different_index();
|
||||
test_constexpr_copy_assignment_non_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
non_constexpr_test();
|
||||
cxx17_constexpr_test();
|
||||
cxx20_constexpr_test();
|
||||
|
||||
static_assert(cxx17_constexpr_test());
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(cxx20_constexpr_test());
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -24,71 +24,70 @@
|
||||
#include "variant_test_helpers.h"
|
||||
|
||||
struct NoCopy {
|
||||
NoCopy(const NoCopy &) = delete;
|
||||
NoCopy &operator=(const NoCopy &) = default;
|
||||
NoCopy(const NoCopy&) = delete;
|
||||
NoCopy& operator=(const NoCopy&) = default;
|
||||
};
|
||||
|
||||
struct CopyOnly {
|
||||
CopyOnly(const CopyOnly &) = default;
|
||||
CopyOnly(CopyOnly &&) = delete;
|
||||
CopyOnly &operator=(const CopyOnly &) = default;
|
||||
CopyOnly &operator=(CopyOnly &&) = delete;
|
||||
CopyOnly(const CopyOnly&) = default;
|
||||
CopyOnly(CopyOnly&&) = delete;
|
||||
CopyOnly& operator=(const CopyOnly&) = default;
|
||||
CopyOnly& operator=(CopyOnly&&) = delete;
|
||||
};
|
||||
|
||||
struct MoveOnly {
|
||||
MoveOnly(const MoveOnly &) = delete;
|
||||
MoveOnly(MoveOnly &&) = default;
|
||||
MoveOnly &operator=(const MoveOnly &) = delete;
|
||||
MoveOnly &operator=(MoveOnly &&) = default;
|
||||
MoveOnly(const MoveOnly&) = delete;
|
||||
MoveOnly(MoveOnly&&) = default;
|
||||
MoveOnly& operator=(const MoveOnly&) = delete;
|
||||
MoveOnly& operator=(MoveOnly&&) = default;
|
||||
};
|
||||
|
||||
struct MoveOnlyNT {
|
||||
MoveOnlyNT(const MoveOnlyNT &) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT &&) {}
|
||||
MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
|
||||
MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
|
||||
MoveOnlyNT(const MoveOnlyNT&) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT&&) {}
|
||||
MoveOnlyNT& operator=(const MoveOnlyNT&) = delete;
|
||||
MoveOnlyNT& operator=(MoveOnlyNT&&) = default;
|
||||
};
|
||||
|
||||
struct MoveOnlyOddNothrow {
|
||||
MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
|
||||
MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
|
||||
MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
|
||||
MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
|
||||
MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {}
|
||||
MoveOnlyOddNothrow(const MoveOnlyOddNothrow&) = delete;
|
||||
MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default;
|
||||
MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&) = delete;
|
||||
};
|
||||
|
||||
struct MoveAssignOnly {
|
||||
MoveAssignOnly(MoveAssignOnly &&) = delete;
|
||||
MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
|
||||
MoveAssignOnly(MoveAssignOnly&&) = delete;
|
||||
MoveAssignOnly& operator=(MoveAssignOnly&&) = default;
|
||||
};
|
||||
|
||||
struct MoveAssign {
|
||||
static int move_construct;
|
||||
static int move_assign;
|
||||
static void reset() { move_construct = move_assign = 0; }
|
||||
MoveAssign(int v) : value(v) {}
|
||||
MoveAssign(MoveAssign &&o) : value(o.value) {
|
||||
++move_construct;
|
||||
constexpr MoveAssign(int v, int* move_ctor, int* move_assi)
|
||||
: value(v), move_construct(move_ctor), move_assign(move_assi) {}
|
||||
constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) {
|
||||
++*move_construct;
|
||||
o.value = -1;
|
||||
}
|
||||
MoveAssign &operator=(MoveAssign &&o) {
|
||||
value = o.value;
|
||||
++move_assign;
|
||||
constexpr MoveAssign& operator=(MoveAssign&& o) {
|
||||
value = o.value;
|
||||
move_construct = o.move_construct;
|
||||
move_assign = o.move_assign;
|
||||
++*move_assign;
|
||||
o.value = -1;
|
||||
return *this;
|
||||
}
|
||||
int value;
|
||||
int* move_construct;
|
||||
int* move_assign;
|
||||
};
|
||||
|
||||
int MoveAssign::move_construct = 0;
|
||||
int MoveAssign::move_assign = 0;
|
||||
|
||||
struct NTMoveAssign {
|
||||
constexpr NTMoveAssign(int v) : value(v) {}
|
||||
NTMoveAssign(const NTMoveAssign &) = default;
|
||||
NTMoveAssign(NTMoveAssign &&) = default;
|
||||
NTMoveAssign &operator=(const NTMoveAssign &that) = default;
|
||||
NTMoveAssign &operator=(NTMoveAssign &&that) {
|
||||
value = that.value;
|
||||
NTMoveAssign(const NTMoveAssign&) = default;
|
||||
NTMoveAssign(NTMoveAssign&&) = default;
|
||||
NTMoveAssign& operator=(const NTMoveAssign& that) = default;
|
||||
NTMoveAssign& operator=(NTMoveAssign&& that) {
|
||||
value = that.value;
|
||||
that.value = -1;
|
||||
return *this;
|
||||
};
|
||||
@ -100,10 +99,10 @@ static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
|
||||
|
||||
struct TMoveAssign {
|
||||
constexpr TMoveAssign(int v) : value(v) {}
|
||||
TMoveAssign(const TMoveAssign &) = delete;
|
||||
TMoveAssign(TMoveAssign &&) = default;
|
||||
TMoveAssign &operator=(const TMoveAssign &) = delete;
|
||||
TMoveAssign &operator=(TMoveAssign &&) = default;
|
||||
TMoveAssign(const TMoveAssign&) = delete;
|
||||
TMoveAssign(TMoveAssign&&) = default;
|
||||
TMoveAssign& operator=(const TMoveAssign&) = delete;
|
||||
TMoveAssign& operator=(TMoveAssign&&) = default;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -111,13 +110,13 @@ static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
|
||||
|
||||
struct TMoveAssignNTCopyAssign {
|
||||
constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
|
||||
TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default;
|
||||
TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default;
|
||||
TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) {
|
||||
TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default;
|
||||
TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&) = default;
|
||||
TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) {
|
||||
value = that.value;
|
||||
return *this;
|
||||
}
|
||||
TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default;
|
||||
TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -127,16 +126,13 @@ struct TrivialCopyNontrivialMove {
|
||||
TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
|
||||
TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
|
||||
TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
|
||||
TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept {
|
||||
return *this;
|
||||
}
|
||||
TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; }
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
|
||||
static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
|
||||
|
||||
|
||||
void test_move_assignment_noexcept() {
|
||||
constexpr void test_move_assignment_noexcept() {
|
||||
{
|
||||
using V = std::variant<int>;
|
||||
static_assert(std::is_nothrow_move_assignable<V>::value, "");
|
||||
@ -163,7 +159,7 @@ void test_move_assignment_noexcept() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_move_assignment_sfinae() {
|
||||
constexpr void test_move_assignment_sfinae() {
|
||||
{
|
||||
using V = std::variant<int, long>;
|
||||
static_assert(std::is_move_assignable<V>::value, "");
|
||||
@ -228,7 +224,7 @@ void test_move_assignment_empty_empty() {
|
||||
makeEmpty(v1);
|
||||
V v2(std::in_place_index<0>);
|
||||
makeEmpty(v2);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
@ -244,7 +240,7 @@ void test_move_assignment_non_empty_empty() {
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>);
|
||||
makeEmpty(v2);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
@ -254,7 +250,7 @@ void test_move_assignment_non_empty_empty() {
|
||||
V v1(std::in_place_index<2>, "hello");
|
||||
V v2(std::in_place_index<0>);
|
||||
makeEmpty(v2);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
@ -270,7 +266,7 @@ void test_move_assignment_empty_non_empty() {
|
||||
V v1(std::in_place_index<0>);
|
||||
makeEmpty(v1);
|
||||
V v2(std::in_place_index<0>, 42);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 0);
|
||||
assert(std::get<0>(v1) == 42);
|
||||
@ -280,7 +276,7 @@ void test_move_assignment_empty_non_empty() {
|
||||
V v1(std::in_place_index<0>);
|
||||
makeEmpty(v1);
|
||||
V v2(std::in_place_type<std::string>, "hello");
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 2);
|
||||
assert(std::get<2>(v1) == "hello");
|
||||
@ -288,14 +284,18 @@ void test_move_assignment_empty_non_empty() {
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
template <typename T> struct Result { std::size_t index; T value; };
|
||||
template <typename T>
|
||||
struct Result {
|
||||
std::size_t index;
|
||||
T value;
|
||||
};
|
||||
|
||||
void test_move_assignment_same_index() {
|
||||
TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() {
|
||||
{
|
||||
using V = std::variant<int>;
|
||||
V v1(43);
|
||||
V v2(42);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 0);
|
||||
assert(std::get<0>(v1) == 42);
|
||||
@ -304,39 +304,24 @@ void test_move_assignment_same_index() {
|
||||
using V = std::variant<int, long, unsigned>;
|
||||
V v1(43l);
|
||||
V v2(42l);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1) == 42);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, MoveAssign, unsigned>;
|
||||
V v1(std::in_place_type<MoveAssign>, 43);
|
||||
V v2(std::in_place_type<MoveAssign>, 42);
|
||||
MoveAssign::reset();
|
||||
V &vref = (v1 = std::move(v2));
|
||||
using V = std::variant<int, MoveAssign, unsigned>;
|
||||
int move_construct = 0;
|
||||
int move_assign = 0;
|
||||
V v1(std::in_place_type<MoveAssign>, 43, &move_construct, &move_assign);
|
||||
V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1).value == 42);
|
||||
assert(MoveAssign::move_construct == 0);
|
||||
assert(MoveAssign::move_assign == 1);
|
||||
assert(move_construct == 0);
|
||||
assert(move_assign == 1);
|
||||
}
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
using MET = MakeEmptyT;
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<MET>);
|
||||
MET &mref = std::get<1>(v1);
|
||||
V v2(std::in_place_type<MET>);
|
||||
try {
|
||||
v1 = std::move(v2);
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
assert(v1.index() == 1);
|
||||
assert(&std::get<1>(v1) == &mref);
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
{
|
||||
@ -383,52 +368,29 @@ void test_move_assignment_same_index() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_move_assignment_different_index() {
|
||||
TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() {
|
||||
{
|
||||
using V = std::variant<int, long, unsigned>;
|
||||
V v1(43);
|
||||
V v2(42l);
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1) == 42);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, MoveAssign, unsigned>;
|
||||
using V = std::variant<int, MoveAssign, unsigned>;
|
||||
int move_construct = 0;
|
||||
int move_assign = 0;
|
||||
V v1(std::in_place_type<unsigned>, 43u);
|
||||
V v2(std::in_place_type<MoveAssign>, 42);
|
||||
MoveAssign::reset();
|
||||
V &vref = (v1 = std::move(v2));
|
||||
V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 1);
|
||||
assert(std::get<1>(v1).value == 42);
|
||||
assert(MoveAssign::move_construct == 1);
|
||||
assert(MoveAssign::move_assign == 0);
|
||||
assert(move_construct == 1);
|
||||
assert(move_assign == 0);
|
||||
}
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
using MET = MakeEmptyT;
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<int>);
|
||||
V v2(std::in_place_type<MET>);
|
||||
try {
|
||||
v1 = std::move(v2);
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<MET>);
|
||||
V v2(std::in_place_type<std::string>, "hello");
|
||||
V &vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 2);
|
||||
assert(std::get<2>(v1) == "hello");
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
{
|
||||
@ -461,38 +423,126 @@ void test_move_assignment_different_index() {
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t NewIdx, class ValueType>
|
||||
constexpr bool test_constexpr_assign_imp(
|
||||
std::variant<long, void*, int>&& v, ValueType&& new_value)
|
||||
{
|
||||
std::variant<long, void*, int> v2(
|
||||
std::forward<ValueType>(new_value));
|
||||
const auto cp = v2;
|
||||
v = std::move(v2);
|
||||
return v.index() == NewIdx &&
|
||||
std::get<NewIdx>(v) == std::get<NewIdx>(cp);
|
||||
void test_assignment_throw() {
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
using MET = MakeEmptyT;
|
||||
// same index
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<MET>);
|
||||
MET& mref = std::get<1>(v1);
|
||||
V v2(std::in_place_type<MET>);
|
||||
try {
|
||||
v1 = std::move(v2);
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
assert(v1.index() == 1);
|
||||
assert(&std::get<1>(v1) == &mref);
|
||||
}
|
||||
|
||||
// different indices
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<int>);
|
||||
V v2(std::in_place_type<MET>);
|
||||
try {
|
||||
v1 = std::move(v2);
|
||||
assert(false);
|
||||
} catch (...) {
|
||||
}
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(v1.index() == std::variant_npos);
|
||||
}
|
||||
{
|
||||
using V = std::variant<int, MET, std::string>;
|
||||
V v1(std::in_place_type<MET>);
|
||||
V v2(std::in_place_type<std::string>, "hello");
|
||||
V& vref = (v1 = std::move(v2));
|
||||
assert(&vref == &v1);
|
||||
assert(v1.index() == 2);
|
||||
assert(std::get<2>(v1) == "hello");
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
void test_constexpr_move_assignment() {
|
||||
template <std::size_t NewIdx, class T, class ValueType>
|
||||
constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {
|
||||
using Variant = std::decay_t<T>;
|
||||
Variant v2(std::forward<ValueType>(new_value));
|
||||
const auto cp = v2;
|
||||
v = std::move(v2);
|
||||
assert(v.index() == NewIdx);
|
||||
assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));
|
||||
}
|
||||
|
||||
constexpr void test_constexpr_move_assignment_trivial() {
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
using V = std::variant<long, void*, int>;
|
||||
static_assert(std::is_trivially_copyable<V>::value, "");
|
||||
static_assert(std::is_trivially_move_assignable<V>::value, "");
|
||||
static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), "");
|
||||
static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), "");
|
||||
static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), "");
|
||||
static_assert(test_constexpr_assign_imp<2>(V(42l), 101), "");
|
||||
test_constexpr_assign_imp<0>(V(42l), 101l);
|
||||
test_constexpr_assign_imp<0>(V(nullptr), 101l);
|
||||
test_constexpr_assign_imp<1>(V(42l), nullptr);
|
||||
test_constexpr_assign_imp<2>(V(42l), 101);
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
struct NonTrivialMoveAssign {
|
||||
int i = 0;
|
||||
constexpr NonTrivialMoveAssign(int ii) : i(ii) {}
|
||||
constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default;
|
||||
constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {}
|
||||
constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default;
|
||||
constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) {
|
||||
i = o.i;
|
||||
return *this;
|
||||
}
|
||||
TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default;
|
||||
friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; }
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() {
|
||||
using V = std::variant<long, void*, NonTrivialMoveAssign>;
|
||||
static_assert(!std::is_trivially_copyable<V>::value);
|
||||
static_assert(!std::is_trivially_move_assignable<V>::value);
|
||||
test_constexpr_assign_imp<0>(V(42l), 101l);
|
||||
test_constexpr_assign_imp<0>(V(nullptr), 101l);
|
||||
test_constexpr_assign_imp<1>(V(42l), nullptr);
|
||||
test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5));
|
||||
test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5));
|
||||
}
|
||||
|
||||
void non_constexpr_test() {
|
||||
test_move_assignment_empty_empty();
|
||||
test_move_assignment_non_empty_empty();
|
||||
test_move_assignment_empty_non_empty();
|
||||
test_move_assignment_same_index();
|
||||
test_move_assignment_different_index();
|
||||
test_assignment_throw();
|
||||
}
|
||||
|
||||
constexpr bool cxx17_constexpr_test() {
|
||||
test_move_assignment_sfinae();
|
||||
test_move_assignment_noexcept();
|
||||
test_constexpr_move_assignment();
|
||||
test_constexpr_move_assignment_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
|
||||
test_move_assignment_same_index();
|
||||
test_move_assignment_different_index();
|
||||
test_constexpr_move_assignment_non_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
non_constexpr_test();
|
||||
cxx17_constexpr_test();
|
||||
cxx20_constexpr_test();
|
||||
|
||||
static_assert(cxx17_constexpr_test());
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(cxx20_constexpr_test());
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,30 +22,30 @@
|
||||
#include "test_workarounds.h"
|
||||
|
||||
struct NonT {
|
||||
NonT(int v) : value(v) {}
|
||||
NonT(const NonT &o) : value(o.value) {}
|
||||
constexpr NonT(int v) : value(v) {}
|
||||
constexpr NonT(const NonT& o) : value(o.value) {}
|
||||
int value;
|
||||
};
|
||||
static_assert(!std::is_trivially_copy_constructible<NonT>::value, "");
|
||||
|
||||
struct NoCopy {
|
||||
NoCopy(const NoCopy &) = delete;
|
||||
NoCopy(const NoCopy&) = delete;
|
||||
};
|
||||
|
||||
struct MoveOnly {
|
||||
MoveOnly(const MoveOnly &) = delete;
|
||||
MoveOnly(MoveOnly &&) = default;
|
||||
MoveOnly(const MoveOnly&) = delete;
|
||||
MoveOnly(MoveOnly&&) = default;
|
||||
};
|
||||
|
||||
struct MoveOnlyNT {
|
||||
MoveOnlyNT(const MoveOnlyNT &) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT &&) {}
|
||||
MoveOnlyNT(const MoveOnlyNT&) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT&&) {}
|
||||
};
|
||||
|
||||
struct NTCopy {
|
||||
constexpr NTCopy(int v) : value(v) {}
|
||||
NTCopy(const NTCopy &that) : value(that.value) {}
|
||||
NTCopy(NTCopy &&) = delete;
|
||||
NTCopy(const NTCopy& that) : value(that.value) {}
|
||||
NTCopy(NTCopy&&) = delete;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -54,8 +54,8 @@ static_assert(std::is_copy_constructible<NTCopy>::value, "");
|
||||
|
||||
struct TCopy {
|
||||
constexpr TCopy(int v) : value(v) {}
|
||||
TCopy(TCopy const &) = default;
|
||||
TCopy(TCopy &&) = delete;
|
||||
TCopy(TCopy const&) = default;
|
||||
TCopy(TCopy&&) = delete;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -74,20 +74,21 @@ static_assert(std::is_trivially_copy_constructible<TCopyNTMove>::value, "");
|
||||
struct MakeEmptyT {
|
||||
static int alive;
|
||||
MakeEmptyT() { ++alive; }
|
||||
MakeEmptyT(const MakeEmptyT &) {
|
||||
MakeEmptyT(const MakeEmptyT&) {
|
||||
++alive;
|
||||
// Don't throw from the copy constructor since variant's assignment
|
||||
// operator performs a copy before committing to the assignment.
|
||||
}
|
||||
MakeEmptyT(MakeEmptyT &&) { throw 42; }
|
||||
MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
|
||||
MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
|
||||
MakeEmptyT(MakeEmptyT&&) { throw 42; }
|
||||
MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
|
||||
MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
|
||||
~MakeEmptyT() { --alive; }
|
||||
};
|
||||
|
||||
int MakeEmptyT::alive = 0;
|
||||
|
||||
template <class Variant> void makeEmpty(Variant &v) {
|
||||
template <class Variant>
|
||||
void makeEmpty(Variant& v) {
|
||||
Variant v2(std::in_place_type<MakeEmptyT>);
|
||||
try {
|
||||
v = std::move(v2);
|
||||
@ -98,7 +99,7 @@ template <class Variant> void makeEmpty(Variant &v) {
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
void test_copy_ctor_sfinae() {
|
||||
constexpr void test_copy_ctor_sfinae() {
|
||||
{
|
||||
using V = std::variant<int, long>;
|
||||
static_assert(std::is_copy_constructible<V>::value, "");
|
||||
@ -136,7 +137,7 @@ void test_copy_ctor_sfinae() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_copy_ctor_basic() {
|
||||
TEST_CONSTEXPR_CXX20 void test_copy_ctor_basic() {
|
||||
{
|
||||
std::variant<int> v(std::in_place_index<0>, 42);
|
||||
std::variant<int> v2 = v;
|
||||
@ -214,21 +215,21 @@ void test_copy_ctor_valueless_by_exception() {
|
||||
using V = std::variant<int, MakeEmptyT>;
|
||||
V v1;
|
||||
makeEmpty(v1);
|
||||
const V &cv1 = v1;
|
||||
const V& cv1 = v1;
|
||||
V v(cv1);
|
||||
assert(v.valueless_by_exception());
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
template <std::size_t Idx>
|
||||
constexpr bool test_constexpr_copy_ctor_imp(std::variant<long, void*, const int> const& v) {
|
||||
template <std::size_t Idx, class T>
|
||||
constexpr void test_constexpr_copy_ctor_imp(const T& v) {
|
||||
auto v2 = v;
|
||||
return v2.index() == v.index() &&
|
||||
v2.index() == Idx &&
|
||||
std::get<Idx>(v2) == std::get<Idx>(v);
|
||||
assert(v2.index() == v.index());
|
||||
assert(v2.index() == Idx);
|
||||
assert(std::get<Idx>(v2) == std::get<Idx>(v));
|
||||
}
|
||||
|
||||
void test_constexpr_copy_ctor() {
|
||||
constexpr void test_constexpr_copy_ctor_trivial() {
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
using V = std::variant<long, void*, const int>;
|
||||
#ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
@ -237,18 +238,57 @@ void test_constexpr_copy_ctor() {
|
||||
static_assert(std::is_trivially_move_constructible<V>::value, "");
|
||||
static_assert(!std::is_copy_assignable<V>::value, "");
|
||||
static_assert(!std::is_move_assignable<V>::value, "");
|
||||
#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_copyable<V>::value, "");
|
||||
#endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(test_constexpr_copy_ctor_imp<0>(V(42l)), "");
|
||||
static_assert(test_constexpr_copy_ctor_imp<1>(V(nullptr)), "");
|
||||
static_assert(test_constexpr_copy_ctor_imp<2>(V(101)), "");
|
||||
static_assert(std::is_trivially_copy_constructible<V>::value, "");
|
||||
test_constexpr_copy_ctor_imp<0>(V(42l));
|
||||
test_constexpr_copy_ctor_imp<1>(V(nullptr));
|
||||
test_constexpr_copy_ctor_imp<2>(V(101));
|
||||
}
|
||||
|
||||
struct NonTrivialCopyCtor {
|
||||
int i = 0;
|
||||
constexpr NonTrivialCopyCtor(int ii) : i(ii) {}
|
||||
constexpr NonTrivialCopyCtor(const NonTrivialCopyCtor& other) : i(other.i) {}
|
||||
constexpr NonTrivialCopyCtor(NonTrivialCopyCtor&& other) = default;
|
||||
TEST_CONSTEXPR_CXX20 ~NonTrivialCopyCtor() = default;
|
||||
friend constexpr bool operator==(const NonTrivialCopyCtor& x, const NonTrivialCopyCtor& y) { return x.i == y.i; }
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_constexpr_copy_ctor_non_trivial() {
|
||||
// Test !is_trivially_move_constructible
|
||||
using V = std::variant<long, NonTrivialCopyCtor, void*>;
|
||||
static_assert(!std::is_trivially_copy_constructible<V>::value, "");
|
||||
test_constexpr_copy_ctor_imp<0>(V(42l));
|
||||
test_constexpr_copy_ctor_imp<1>(V(NonTrivialCopyCtor(5)));
|
||||
test_constexpr_copy_ctor_imp<2>(V(nullptr));
|
||||
}
|
||||
|
||||
void non_constexpr_test() { test_copy_ctor_valueless_by_exception(); }
|
||||
|
||||
constexpr bool cxx17_constexpr_test() {
|
||||
test_copy_ctor_sfinae();
|
||||
test_constexpr_copy_ctor_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
|
||||
test_copy_ctor_basic();
|
||||
test_constexpr_copy_ctor_non_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test_copy_ctor_basic();
|
||||
test_copy_ctor_valueless_by_exception();
|
||||
test_copy_ctor_sfinae();
|
||||
test_constexpr_copy_ctor();
|
||||
non_constexpr_test();
|
||||
cxx17_constexpr_test();
|
||||
cxx20_constexpr_test();
|
||||
|
||||
static_assert(cxx17_constexpr_test());
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(cxx20_constexpr_test());
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <string>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "variant_test_helpers.h"
|
||||
@ -35,7 +36,7 @@ struct DefaultCtorThrows {
|
||||
};
|
||||
#endif
|
||||
|
||||
void test_default_ctor_sfinae() {
|
||||
constexpr void test_default_ctor_sfinae() {
|
||||
{
|
||||
using V = std::variant<std::monostate, int>;
|
||||
static_assert(std::is_default_constructible<V>::value, "");
|
||||
@ -46,7 +47,7 @@ void test_default_ctor_sfinae() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_default_ctor_noexcept() {
|
||||
constexpr void test_default_ctor_noexcept() {
|
||||
{
|
||||
using V = std::variant<int>;
|
||||
static_assert(std::is_nothrow_default_constructible<V>::value, "");
|
||||
@ -63,7 +64,7 @@ void test_default_ctor_throws() {
|
||||
try {
|
||||
V v;
|
||||
assert(false);
|
||||
} catch (const int &ex) {
|
||||
} catch (const int& ex) {
|
||||
assert(ex == 42);
|
||||
} catch (...) {
|
||||
assert(false);
|
||||
@ -71,7 +72,7 @@ void test_default_ctor_throws() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_default_ctor_basic() {
|
||||
constexpr void test_default_ctor_basic() {
|
||||
{
|
||||
std::variant<int> v;
|
||||
assert(v.index() == 0);
|
||||
@ -107,11 +108,24 @@ void test_default_ctor_basic() {
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
constexpr void issue_86686() {
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(std::variant<std::string>{}.index() == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr bool test() {
|
||||
test_default_ctor_basic();
|
||||
test_default_ctor_sfinae();
|
||||
test_default_ctor_noexcept();
|
||||
test_default_ctor_throws();
|
||||
issue_86686();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
test_default_ctor_throws();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,31 +23,31 @@
|
||||
#include "test_workarounds.h"
|
||||
|
||||
struct ThrowsMove {
|
||||
ThrowsMove(ThrowsMove &&) noexcept(false) {}
|
||||
ThrowsMove(ThrowsMove&&) noexcept(false) {}
|
||||
};
|
||||
|
||||
struct NoCopy {
|
||||
NoCopy(const NoCopy &) = delete;
|
||||
NoCopy(const NoCopy&) = delete;
|
||||
};
|
||||
|
||||
struct MoveOnly {
|
||||
int value;
|
||||
MoveOnly(int v) : value(v) {}
|
||||
MoveOnly(const MoveOnly &) = delete;
|
||||
MoveOnly(MoveOnly &&) = default;
|
||||
constexpr MoveOnly(int v) : value(v) {}
|
||||
MoveOnly(const MoveOnly&) = delete;
|
||||
MoveOnly(MoveOnly&&) = default;
|
||||
};
|
||||
|
||||
struct MoveOnlyNT {
|
||||
int value;
|
||||
MoveOnlyNT(int v) : value(v) {}
|
||||
MoveOnlyNT(const MoveOnlyNT &) = delete;
|
||||
MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; }
|
||||
constexpr MoveOnlyNT(int v) : value(v) {}
|
||||
MoveOnlyNT(const MoveOnlyNT&) = delete;
|
||||
constexpr MoveOnlyNT(MoveOnlyNT&& other) : value(other.value) { other.value = -1; }
|
||||
};
|
||||
|
||||
struct NTMove {
|
||||
constexpr NTMove(int v) : value(v) {}
|
||||
NTMove(const NTMove &) = delete;
|
||||
NTMove(NTMove &&that) : value(that.value) { that.value = -1; }
|
||||
NTMove(const NTMove&) = delete;
|
||||
NTMove(NTMove&& that) : value(that.value) { that.value = -1; }
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -56,8 +56,8 @@ static_assert(std::is_move_constructible<NTMove>::value, "");
|
||||
|
||||
struct TMove {
|
||||
constexpr TMove(int v) : value(v) {}
|
||||
TMove(const TMove &) = delete;
|
||||
TMove(TMove &&) = default;
|
||||
TMove(const TMove&) = delete;
|
||||
TMove(TMove&&) = default;
|
||||
int value;
|
||||
};
|
||||
|
||||
@ -76,20 +76,21 @@ static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
|
||||
struct MakeEmptyT {
|
||||
static int alive;
|
||||
MakeEmptyT() { ++alive; }
|
||||
MakeEmptyT(const MakeEmptyT &) {
|
||||
MakeEmptyT(const MakeEmptyT&) {
|
||||
++alive;
|
||||
// Don't throw from the copy constructor since variant's assignment
|
||||
// operator performs a copy before committing to the assignment.
|
||||
}
|
||||
MakeEmptyT(MakeEmptyT &&) { throw 42; }
|
||||
MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
|
||||
MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
|
||||
MakeEmptyT(MakeEmptyT&&) { throw 42; }
|
||||
MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; }
|
||||
MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
|
||||
~MakeEmptyT() { --alive; }
|
||||
};
|
||||
|
||||
int MakeEmptyT::alive = 0;
|
||||
|
||||
template <class Variant> void makeEmpty(Variant &v) {
|
||||
template <class Variant>
|
||||
void makeEmpty(Variant& v) {
|
||||
Variant v2(std::in_place_type<MakeEmptyT>);
|
||||
try {
|
||||
v = std::move(v2);
|
||||
@ -100,7 +101,7 @@ template <class Variant> void makeEmpty(Variant &v) {
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
void test_move_noexcept() {
|
||||
constexpr void test_move_noexcept() {
|
||||
{
|
||||
using V = std::variant<int, long>;
|
||||
static_assert(std::is_nothrow_move_constructible<V>::value, "");
|
||||
@ -119,7 +120,7 @@ void test_move_noexcept() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_move_ctor_sfinae() {
|
||||
constexpr void test_move_ctor_sfinae() {
|
||||
{
|
||||
using V = std::variant<int, long>;
|
||||
static_assert(std::is_move_constructible<V>::value, "");
|
||||
@ -158,9 +159,12 @@ void test_move_ctor_sfinae() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Result { std::size_t index; T value; };
|
||||
struct Result {
|
||||
std::size_t index;
|
||||
T value;
|
||||
};
|
||||
|
||||
void test_move_ctor_basic() {
|
||||
TEST_CONSTEXPR_CXX20 void test_move_ctor_basic() {
|
||||
{
|
||||
std::variant<int> v(std::in_place_index<0>, 42);
|
||||
std::variant<int> v2 = std::move(v);
|
||||
@ -289,16 +293,16 @@ void test_move_ctor_valueless_by_exception() {
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
}
|
||||
|
||||
template <std::size_t Idx>
|
||||
constexpr bool test_constexpr_ctor_imp(std::variant<long, void*, const int> const& v) {
|
||||
template <std::size_t Idx, class T>
|
||||
constexpr void test_constexpr_ctor_imp(const T& v) {
|
||||
auto copy = v;
|
||||
auto v2 = std::move(copy);
|
||||
return v2.index() == v.index() &&
|
||||
v2.index() == Idx &&
|
||||
std::get<Idx>(v2) == std::get<Idx>(v);
|
||||
auto v2 = std::move(copy);
|
||||
assert(v2.index() == v.index());
|
||||
assert(v2.index() == Idx);
|
||||
assert(std::get<Idx>(v2) == std::get<Idx>(v));
|
||||
}
|
||||
|
||||
void test_constexpr_move_ctor() {
|
||||
constexpr void test_constexpr_move_ctor_trivial() {
|
||||
// Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
|
||||
using V = std::variant<long, void*, const int>;
|
||||
#ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
@ -307,21 +311,58 @@ void test_constexpr_move_ctor() {
|
||||
static_assert(std::is_trivially_move_constructible<V>::value, "");
|
||||
static_assert(!std::is_copy_assignable<V>::value, "");
|
||||
static_assert(!std::is_move_assignable<V>::value, "");
|
||||
#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_copyable<V>::value, "");
|
||||
#endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE
|
||||
static_assert(std::is_trivially_move_constructible<V>::value, "");
|
||||
static_assert(test_constexpr_ctor_imp<0>(V(42l)), "");
|
||||
static_assert(test_constexpr_ctor_imp<1>(V(nullptr)), "");
|
||||
static_assert(test_constexpr_ctor_imp<2>(V(101)), "");
|
||||
test_constexpr_ctor_imp<0>(V(42l));
|
||||
test_constexpr_ctor_imp<1>(V(nullptr));
|
||||
test_constexpr_ctor_imp<2>(V(101));
|
||||
}
|
||||
|
||||
struct NonTrivialMoveCtor {
|
||||
int i = 0;
|
||||
constexpr NonTrivialMoveCtor(int ii) : i(ii) {}
|
||||
constexpr NonTrivialMoveCtor(const NonTrivialMoveCtor& other) = default;
|
||||
constexpr NonTrivialMoveCtor(NonTrivialMoveCtor&& other) : i(other.i) {}
|
||||
TEST_CONSTEXPR_CXX20 ~NonTrivialMoveCtor() = default;
|
||||
friend constexpr bool operator==(const NonTrivialMoveCtor& x, const NonTrivialMoveCtor& y) { return x.i == y.i; }
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_constexpr_move_ctor_non_trivial() {
|
||||
using V = std::variant<long, NonTrivialMoveCtor, void*>;
|
||||
static_assert(!std::is_trivially_move_constructible<V>::value, "");
|
||||
test_constexpr_ctor_imp<0>(V(42l));
|
||||
test_constexpr_ctor_imp<1>(V(NonTrivialMoveCtor(5)));
|
||||
test_constexpr_ctor_imp<2>(V(nullptr));
|
||||
}
|
||||
|
||||
void non_constexpr_test() { test_move_ctor_valueless_by_exception(); }
|
||||
|
||||
constexpr bool cxx17_constexpr_test() {
|
||||
test_move_noexcept();
|
||||
test_move_ctor_sfinae();
|
||||
test_constexpr_move_ctor_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {
|
||||
test_move_ctor_basic();
|
||||
test_constexpr_move_ctor_non_trivial();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test_move_ctor_basic();
|
||||
test_move_ctor_valueless_by_exception();
|
||||
test_move_noexcept();
|
||||
test_move_ctor_sfinae();
|
||||
test_constexpr_move_ctor();
|
||||
non_constexpr_test();
|
||||
cxx17_constexpr_test();
|
||||
cxx20_constexpr_test();
|
||||
|
||||
static_assert(cxx17_constexpr_test());
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(cxx20_constexpr_test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,55 +21,76 @@
|
||||
#include "test_macros.h"
|
||||
|
||||
struct NonTDtor {
|
||||
static int count;
|
||||
NonTDtor() = default;
|
||||
~NonTDtor() { ++count; }
|
||||
int* count;
|
||||
constexpr NonTDtor(int* a, int*) : count(a) {}
|
||||
TEST_CONSTEXPR_CXX20 ~NonTDtor() { ++*count; }
|
||||
};
|
||||
int NonTDtor::count = 0;
|
||||
static_assert(!std::is_trivially_destructible<NonTDtor>::value, "");
|
||||
|
||||
struct NonTDtor1 {
|
||||
static int count;
|
||||
NonTDtor1() = default;
|
||||
~NonTDtor1() { ++count; }
|
||||
int* count;
|
||||
constexpr NonTDtor1(int*, int* b) : count(b) {}
|
||||
TEST_CONSTEXPR_CXX20 ~NonTDtor1() { ++*count; }
|
||||
};
|
||||
int NonTDtor1::count = 0;
|
||||
static_assert(!std::is_trivially_destructible<NonTDtor1>::value, "");
|
||||
|
||||
struct TDtor {
|
||||
TDtor(const TDtor &) {} // non-trivial copy
|
||||
~TDtor() = default;
|
||||
constexpr TDtor() = default;
|
||||
constexpr TDtor(const TDtor&) {} // non-trivial copy
|
||||
TEST_CONSTEXPR_CXX20 ~TDtor() = default;
|
||||
};
|
||||
static_assert(!std::is_trivially_copy_constructible<TDtor>::value, "");
|
||||
static_assert(std::is_trivially_destructible<TDtor>::value, "");
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
{
|
||||
using V = std::variant<int, long, TDtor>;
|
||||
static_assert(std::is_trivially_destructible<V>::value, "");
|
||||
[[maybe_unused]] V v(std::in_place_index<2>);
|
||||
}
|
||||
{
|
||||
using V = std::variant<NonTDtor, int, NonTDtor1>;
|
||||
static_assert(!std::is_trivially_destructible<V>::value, "");
|
||||
{
|
||||
V v(std::in_place_index<0>);
|
||||
assert(NonTDtor::count == 0);
|
||||
assert(NonTDtor1::count == 0);
|
||||
int count0 = 0;
|
||||
int count1 = 0;
|
||||
{
|
||||
V v(std::in_place_index<0>, &count0, &count1);
|
||||
assert(count0 == 0);
|
||||
assert(count1 == 0);
|
||||
}
|
||||
assert(count0 == 1);
|
||||
assert(count1 == 0);
|
||||
}
|
||||
assert(NonTDtor::count == 1);
|
||||
assert(NonTDtor1::count == 0);
|
||||
NonTDtor::count = 0;
|
||||
{ V v(std::in_place_index<1>); }
|
||||
assert(NonTDtor::count == 0);
|
||||
assert(NonTDtor1::count == 0);
|
||||
{
|
||||
V v(std::in_place_index<2>);
|
||||
assert(NonTDtor::count == 0);
|
||||
assert(NonTDtor1::count == 0);
|
||||
int count0 = 0;
|
||||
int count1 = 0;
|
||||
{ V v(std::in_place_index<1>); }
|
||||
assert(count0 == 0);
|
||||
assert(count1 == 0);
|
||||
}
|
||||
{
|
||||
int count0 = 0;
|
||||
int count1 = 0;
|
||||
{
|
||||
V v(std::in_place_index<2>, &count0, &count1);
|
||||
assert(count0 == 0);
|
||||
assert(count1 == 0);
|
||||
}
|
||||
assert(count0 == 0);
|
||||
assert(count1 == 1);
|
||||
}
|
||||
assert(NonTDtor::count == 0);
|
||||
assert(NonTDtor1::count == 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -26,8 +26,8 @@
|
||||
#include "variant_test_helpers.h"
|
||||
|
||||
template <class Var, std::size_t I, class... Args>
|
||||
constexpr auto test_emplace_exists_imp(int) -> decltype(
|
||||
std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
|
||||
constexpr auto test_emplace_exists_imp(int)
|
||||
-> decltype(std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -36,28 +36,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Var, std::size_t I, class... Args> constexpr bool emplace_exists() {
|
||||
template <class Var, std::size_t I, class... Args>
|
||||
constexpr bool emplace_exists() {
|
||||
return test_emplace_exists_imp<Var, I, Args...>(0);
|
||||
}
|
||||
|
||||
void test_emplace_sfinae() {
|
||||
constexpr void test_emplace_sfinae() {
|
||||
{
|
||||
using V = std::variant<int, void *, const void *, TestTypes::NoCtors>;
|
||||
using V = std::variant<int, void*, const void*, TestTypes::NoCtors>;
|
||||
static_assert(emplace_exists<V, 0>(), "");
|
||||
static_assert(emplace_exists<V, 0, int>(), "");
|
||||
static_assert(!emplace_exists<V, 0, decltype(nullptr)>(),
|
||||
"cannot construct");
|
||||
static_assert(!emplace_exists<V, 0, decltype(nullptr)>(), "cannot construct");
|
||||
static_assert(emplace_exists<V, 1, decltype(nullptr)>(), "");
|
||||
static_assert(emplace_exists<V, 1, int *>(), "");
|
||||
static_assert(!emplace_exists<V, 1, const int *>(), "");
|
||||
static_assert(emplace_exists<V, 1, int*>(), "");
|
||||
static_assert(!emplace_exists<V, 1, const int*>(), "");
|
||||
static_assert(!emplace_exists<V, 1, int>(), "cannot construct");
|
||||
static_assert(emplace_exists<V, 2, const int *>(), "");
|
||||
static_assert(emplace_exists<V, 2, int *>(), "");
|
||||
static_assert(emplace_exists<V, 2, const int*>(), "");
|
||||
static_assert(emplace_exists<V, 2, int*>(), "");
|
||||
static_assert(!emplace_exists<V, 3>(), "cannot construct");
|
||||
}
|
||||
}
|
||||
|
||||
void test_basic() {
|
||||
struct NoCtor {
|
||||
NoCtor() = delete;
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_basic() {
|
||||
{
|
||||
using V = std::variant<int>;
|
||||
V v(42);
|
||||
@ -70,9 +74,9 @@ void test_basic() {
|
||||
assert(std::get<0>(v) == 42);
|
||||
assert(&ref2 == &std::get<0>(v));
|
||||
}
|
||||
|
||||
{
|
||||
using V =
|
||||
std::variant<int, long, const void *, TestTypes::NoCtors, std::string>;
|
||||
using V = std::variant<int, long, const void*, NoCtor, std::string>;
|
||||
const int x = 100;
|
||||
V v(std::in_place_index<0>, -1);
|
||||
// default emplace a value
|
||||
@ -92,9 +96,19 @@ void test_basic() {
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test_basic();
|
||||
test_emplace_sfinae();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -32,13 +32,12 @@ struct InitList {
|
||||
struct InitListArg {
|
||||
std::size_t size;
|
||||
int value;
|
||||
constexpr InitListArg(std::initializer_list<int> il, int v)
|
||||
: size(il.size()), value(v) {}
|
||||
constexpr InitListArg(std::initializer_list<int> il, int v) : size(il.size()), value(v) {}
|
||||
};
|
||||
|
||||
template <class Var, std::size_t I, class... Args>
|
||||
constexpr auto test_emplace_exists_imp(int) -> decltype(
|
||||
std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
|
||||
constexpr auto test_emplace_exists_imp(int)
|
||||
-> decltype(std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -47,13 +46,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Var, std::size_t I, class... Args> constexpr bool emplace_exists() {
|
||||
template <class Var, std::size_t I, class... Args>
|
||||
constexpr bool emplace_exists() {
|
||||
return test_emplace_exists_imp<Var, I, Args...>(0);
|
||||
}
|
||||
|
||||
void test_emplace_sfinae() {
|
||||
using V =
|
||||
std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
|
||||
constexpr void test_emplace_sfinae() {
|
||||
using V = std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
|
||||
using IL = std::initializer_list<int>;
|
||||
static_assert(!emplace_exists<V, 1, IL>(), "no such constructor");
|
||||
static_assert(emplace_exists<V, 2, IL>(), "");
|
||||
@ -65,8 +64,12 @@ void test_emplace_sfinae() {
|
||||
static_assert(!emplace_exists<V, 3, IL, int, int>(), "too many args");
|
||||
}
|
||||
|
||||
void test_basic() {
|
||||
using V = std::variant<int, InitList, InitListArg, TestTypes::NoCtors>;
|
||||
struct NoCtor {
|
||||
NoCtor() = delete;
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_basic() {
|
||||
using V = std::variant<int, InitList, InitListArg, NoCtor>;
|
||||
V v;
|
||||
auto& ref1 = v.emplace<1>({1, 2, 3});
|
||||
static_assert(std::is_same_v<InitList&, decltype(ref1)>, "");
|
||||
@ -83,9 +86,19 @@ void test_basic() {
|
||||
assert(&ref3 == &std::get<1>(v));
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test_basic();
|
||||
test_emplace_sfinae();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
#include "variant_test_helpers.h"
|
||||
|
||||
template <class Var, class T, class... Args>
|
||||
constexpr auto test_emplace_exists_imp(int) -> decltype(
|
||||
std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
|
||||
constexpr auto test_emplace_exists_imp(int)
|
||||
-> decltype(std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -35,28 +35,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class... Args> constexpr bool emplace_exists() {
|
||||
template <class... Args>
|
||||
constexpr bool emplace_exists() {
|
||||
return test_emplace_exists_imp<Args...>(0);
|
||||
}
|
||||
|
||||
void test_emplace_sfinae() {
|
||||
constexpr void test_emplace_sfinae() {
|
||||
{
|
||||
using V = std::variant<int, void *, const void *, TestTypes::NoCtors>;
|
||||
using V = std::variant<int, void*, const void*, TestTypes::NoCtors>;
|
||||
static_assert(emplace_exists<V, int>(), "");
|
||||
static_assert(emplace_exists<V, int, int>(), "");
|
||||
static_assert(!emplace_exists<V, int, decltype(nullptr)>(),
|
||||
"cannot construct");
|
||||
static_assert(emplace_exists<V, void *, decltype(nullptr)>(), "");
|
||||
static_assert(!emplace_exists<V, void *, int>(), "cannot construct");
|
||||
static_assert(emplace_exists<V, void *, int *>(), "");
|
||||
static_assert(!emplace_exists<V, void *, const int *>(), "");
|
||||
static_assert(emplace_exists<V, const void *, const int *>(), "");
|
||||
static_assert(emplace_exists<V, const void *, int *>(), "");
|
||||
static_assert(!emplace_exists<V, int, decltype(nullptr)>(), "cannot construct");
|
||||
static_assert(emplace_exists<V, void*, decltype(nullptr)>(), "");
|
||||
static_assert(!emplace_exists<V, void*, int>(), "cannot construct");
|
||||
static_assert(emplace_exists<V, void*, int*>(), "");
|
||||
static_assert(!emplace_exists<V, void*, const int*>(), "");
|
||||
static_assert(emplace_exists<V, const void*, const int*>(), "");
|
||||
static_assert(emplace_exists<V, const void*, int*>(), "");
|
||||
static_assert(!emplace_exists<V, TestTypes::NoCtors>(), "cannot construct");
|
||||
}
|
||||
}
|
||||
|
||||
void test_basic() {
|
||||
struct NoCtor {
|
||||
NoCtor() = delete;
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_basic() {
|
||||
{
|
||||
using V = std::variant<int>;
|
||||
V v(42);
|
||||
@ -70,8 +74,7 @@ void test_basic() {
|
||||
assert(&ref2 == &std::get<0>(v));
|
||||
}
|
||||
{
|
||||
using V =
|
||||
std::variant<int, long, const void *, TestTypes::NoCtors, std::string>;
|
||||
using V = std::variant<int, long, const void*, NoCtor, std::string>;
|
||||
const int x = 100;
|
||||
V v(std::in_place_type<int>, -1);
|
||||
// default emplace a value
|
||||
@ -79,8 +82,8 @@ void test_basic() {
|
||||
static_assert(std::is_same_v<long&, decltype(ref1)>, "");
|
||||
assert(std::get<1>(v) == 0);
|
||||
assert(&ref1 == &std::get<1>(v));
|
||||
auto& ref2 = v.emplace<const void *>(&x);
|
||||
static_assert(std::is_same_v<const void *&, decltype(ref2)>, "");
|
||||
auto& ref2 = v.emplace<const void*>(&x);
|
||||
static_assert(std::is_same_v<const void*&, decltype(ref2)>, "");
|
||||
assert(std::get<2>(v) == &x);
|
||||
assert(&ref2 == &std::get<2>(v));
|
||||
// emplace with multiple args
|
||||
@ -91,9 +94,19 @@ void test_basic() {
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test_basic();
|
||||
test_emplace_sfinae();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -32,13 +32,12 @@ struct InitList {
|
||||
struct InitListArg {
|
||||
std::size_t size;
|
||||
int value;
|
||||
constexpr InitListArg(std::initializer_list<int> il, int v)
|
||||
: size(il.size()), value(v) {}
|
||||
constexpr InitListArg(std::initializer_list<int> il, int v) : size(il.size()), value(v) {}
|
||||
};
|
||||
|
||||
template <class Var, class T, class... Args>
|
||||
constexpr auto test_emplace_exists_imp(int) -> decltype(
|
||||
std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
|
||||
constexpr auto test_emplace_exists_imp(int)
|
||||
-> decltype(std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -47,13 +46,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class... Args> constexpr bool emplace_exists() {
|
||||
template <class... Args>
|
||||
constexpr bool emplace_exists() {
|
||||
return test_emplace_exists_imp<Args...>(0);
|
||||
}
|
||||
|
||||
void test_emplace_sfinae() {
|
||||
using V =
|
||||
std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
|
||||
constexpr void test_emplace_sfinae() {
|
||||
using V = std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>;
|
||||
using IL = std::initializer_list<int>;
|
||||
static_assert(emplace_exists<V, InitList, IL>(), "");
|
||||
static_assert(!emplace_exists<V, InitList, int>(), "args don't match");
|
||||
@ -61,31 +60,44 @@ void test_emplace_sfinae() {
|
||||
static_assert(emplace_exists<V, InitListArg, IL, int>(), "");
|
||||
static_assert(!emplace_exists<V, InitListArg, int>(), "args don't match");
|
||||
static_assert(!emplace_exists<V, InitListArg, IL>(), "too few args");
|
||||
static_assert(!emplace_exists<V, InitListArg, IL, int, int>(),
|
||||
"too many args");
|
||||
static_assert(!emplace_exists<V, InitListArg, IL, int, int>(), "too many args");
|
||||
}
|
||||
|
||||
void test_basic() {
|
||||
using V = std::variant<int, InitList, InitListArg, TestTypes::NoCtors>;
|
||||
struct NoCtor {
|
||||
NoCtor() = delete;
|
||||
};
|
||||
|
||||
TEST_CONSTEXPR_CXX20 void test_basic() {
|
||||
using V = std::variant<int, InitList, InitListArg, NoCtor>;
|
||||
V v;
|
||||
auto& ref1 = v.emplace<InitList>({1, 2, 3});
|
||||
static_assert(std::is_same_v<InitList&,decltype(ref1)>, "");
|
||||
static_assert(std::is_same_v<InitList&, decltype(ref1)>, "");
|
||||
assert(std::get<InitList>(v).size == 3);
|
||||
assert(&ref1 == &std::get<InitList>(v));
|
||||
auto& ref2 = v.emplace<InitListArg>({1, 2, 3, 4}, 42);
|
||||
static_assert(std::is_same_v<InitListArg&,decltype(ref2)>, "");
|
||||
static_assert(std::is_same_v<InitListArg&, decltype(ref2)>, "");
|
||||
assert(std::get<InitListArg>(v).size == 4);
|
||||
assert(std::get<InitListArg>(v).value == 42);
|
||||
assert(&ref2 == &std::get<InitListArg>(v));
|
||||
auto& ref3 = v.emplace<InitList>({1});
|
||||
static_assert(std::is_same_v<InitList&,decltype(ref3)>, "");
|
||||
static_assert(std::is_same_v<InitList&, decltype(ref3)>, "");
|
||||
assert(std::get<InitList>(v).size == 1);
|
||||
assert(&ref3 == &std::get<InitList>(v));
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test_basic();
|
||||
test_emplace_sfinae();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -25,37 +25,39 @@
|
||||
#include "variant_test_helpers.h"
|
||||
|
||||
struct NotSwappable {};
|
||||
void swap(NotSwappable &, NotSwappable &) = delete;
|
||||
void swap(NotSwappable&, NotSwappable&) = delete;
|
||||
|
||||
struct NotCopyable {
|
||||
NotCopyable() = default;
|
||||
NotCopyable(const NotCopyable &) = delete;
|
||||
NotCopyable &operator=(const NotCopyable &) = delete;
|
||||
NotCopyable() = default;
|
||||
NotCopyable(const NotCopyable&) = delete;
|
||||
NotCopyable& operator=(const NotCopyable&) = delete;
|
||||
};
|
||||
|
||||
struct NotCopyableWithSwap {
|
||||
NotCopyableWithSwap() = default;
|
||||
NotCopyableWithSwap(const NotCopyableWithSwap &) = delete;
|
||||
NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete;
|
||||
NotCopyableWithSwap() = default;
|
||||
NotCopyableWithSwap(const NotCopyableWithSwap&) = delete;
|
||||
NotCopyableWithSwap& operator=(const NotCopyableWithSwap&) = delete;
|
||||
};
|
||||
void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {}
|
||||
constexpr void swap(NotCopyableWithSwap&, NotCopyableWithSwap) {}
|
||||
|
||||
struct NotMoveAssignable {
|
||||
NotMoveAssignable() = default;
|
||||
NotMoveAssignable(NotMoveAssignable &&) = default;
|
||||
NotMoveAssignable &operator=(NotMoveAssignable &&) = delete;
|
||||
NotMoveAssignable() = default;
|
||||
NotMoveAssignable(NotMoveAssignable&&) = default;
|
||||
NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;
|
||||
};
|
||||
|
||||
struct NotMoveAssignableWithSwap {
|
||||
NotMoveAssignableWithSwap() = default;
|
||||
NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default;
|
||||
NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete;
|
||||
NotMoveAssignableWithSwap() = default;
|
||||
NotMoveAssignableWithSwap(NotMoveAssignableWithSwap&&) = default;
|
||||
NotMoveAssignableWithSwap& operator=(NotMoveAssignableWithSwap&&) = delete;
|
||||
};
|
||||
void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {}
|
||||
constexpr void swap(NotMoveAssignableWithSwap&, NotMoveAssignableWithSwap&) noexcept {}
|
||||
|
||||
template <bool Throws> void do_throw() {}
|
||||
template <bool Throws>
|
||||
constexpr void do_throw() {}
|
||||
|
||||
template <> void do_throw<true>() {
|
||||
template <>
|
||||
void do_throw<true>() {
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
throw 42;
|
||||
#else
|
||||
@ -63,60 +65,49 @@ template <> void do_throw<true>() {
|
||||
#endif
|
||||
}
|
||||
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
|
||||
bool NT_Swap, bool EnableSwap = true>
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap, bool EnableSwap = true>
|
||||
struct NothrowTypeImp {
|
||||
static int move_called;
|
||||
static int move_assign_called;
|
||||
static int swap_called;
|
||||
static void reset() { move_called = move_assign_called = swap_called = 0; }
|
||||
NothrowTypeImp() = default;
|
||||
explicit NothrowTypeImp(int v) : value(v) {}
|
||||
NothrowTypeImp(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) {
|
||||
assert(false);
|
||||
} // never called by test
|
||||
NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) {
|
||||
++move_called;
|
||||
int value;
|
||||
int* move_called;
|
||||
int* move_assign_called;
|
||||
int* swap_called;
|
||||
|
||||
constexpr NothrowTypeImp(int v, int* mv_ctr, int* mv_assign, int* swap)
|
||||
: value(v), move_called(mv_ctr), move_assign_called(mv_assign), swap_called(swap) {}
|
||||
|
||||
NothrowTypeImp(const NothrowTypeImp& o) noexcept(NT_Copy) : value(o.value) { assert(false); } // never called by test
|
||||
|
||||
constexpr NothrowTypeImp(NothrowTypeImp&& o) noexcept(NT_Move)
|
||||
: value(o.value),
|
||||
move_called(o.move_called),
|
||||
move_assign_called(o.move_assign_called),
|
||||
swap_called(o.swap_called) {
|
||||
++*move_called;
|
||||
do_throw<!NT_Move>();
|
||||
o.value = -1;
|
||||
}
|
||||
NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) {
|
||||
|
||||
NothrowTypeImp& operator=(const NothrowTypeImp&) noexcept(NT_CopyAssign) {
|
||||
assert(false);
|
||||
return *this;
|
||||
} // never called by the tests
|
||||
NothrowTypeImp &operator=(NothrowTypeImp &&o) noexcept(NT_MoveAssign) {
|
||||
++move_assign_called;
|
||||
|
||||
constexpr NothrowTypeImp& operator=(NothrowTypeImp&& o) noexcept(NT_MoveAssign) {
|
||||
++*move_assign_called;
|
||||
do_throw<!NT_MoveAssign>();
|
||||
value = o.value;
|
||||
value = o.value;
|
||||
o.value = -1;
|
||||
return *this;
|
||||
}
|
||||
int value;
|
||||
};
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
|
||||
bool NT_Swap, bool EnableSwap>
|
||||
int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
|
||||
EnableSwap>::move_called = 0;
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
|
||||
bool NT_Swap, bool EnableSwap>
|
||||
int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
|
||||
EnableSwap>::move_assign_called = 0;
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
|
||||
bool NT_Swap, bool EnableSwap>
|
||||
int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap,
|
||||
EnableSwap>::swap_called = 0;
|
||||
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign,
|
||||
bool NT_Swap>
|
||||
void swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign,
|
||||
NT_Swap, true> &lhs,
|
||||
NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign,
|
||||
NT_Swap, true> &rhs) noexcept(NT_Swap) {
|
||||
lhs.swap_called++;
|
||||
template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, bool NT_Swap>
|
||||
constexpr void
|
||||
swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& lhs,
|
||||
NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, true>& rhs) noexcept(NT_Swap) {
|
||||
++*lhs.swap_called;
|
||||
do_throw<!NT_Swap>();
|
||||
int tmp = lhs.value;
|
||||
lhs.value = rhs.value;
|
||||
rhs.value = tmp;
|
||||
std::swap(lhs.value, rhs.value);
|
||||
}
|
||||
|
||||
// throwing copy, nothrow move ctor/assign, no swap provided
|
||||
@ -124,53 +115,42 @@ using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>;
|
||||
// throwing copy and move assign, nothrow move ctor, no swap provided
|
||||
using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
|
||||
// nothrow move ctor, throwing move assignment, swap provided
|
||||
using NothrowMoveCtorWithThrowingSwap =
|
||||
NothrowTypeImp<false, true, false, false, false, true>;
|
||||
using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp<false, true, false, false, false, true>;
|
||||
// throwing move ctor, nothrow move assignment, no swap provided
|
||||
using ThrowingMoveCtor =
|
||||
NothrowTypeImp<false, false, false, true, false, false>;
|
||||
using ThrowingMoveCtor = NothrowTypeImp<false, false, false, true, false, false>;
|
||||
// throwing special members, nothrowing swap
|
||||
using ThrowingTypeWithNothrowSwap =
|
||||
NothrowTypeImp<false, false, false, false, true, true>;
|
||||
using NothrowTypeWithThrowingSwap =
|
||||
NothrowTypeImp<true, true, true, true, false, true>;
|
||||
using ThrowingTypeWithNothrowSwap = NothrowTypeImp<false, false, false, false, true, true>;
|
||||
using NothrowTypeWithThrowingSwap = NothrowTypeImp<true, true, true, true, false, true>;
|
||||
// throwing move assign with nothrow move and nothrow swap
|
||||
using ThrowingMoveAssignNothrowMoveCtorWithSwap =
|
||||
NothrowTypeImp<false, true, false, false, true, true>;
|
||||
using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp<false, true, false, false, true, true>;
|
||||
// throwing move assign with nothrow move but no swap.
|
||||
using ThrowingMoveAssignNothrowMoveCtor =
|
||||
NothrowTypeImp<false, true, false, false, false, false>;
|
||||
using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>;
|
||||
|
||||
struct NonThrowingNonNoexceptType {
|
||||
static int move_called;
|
||||
static void reset() { move_called = 0; }
|
||||
NonThrowingNonNoexceptType() = default;
|
||||
NonThrowingNonNoexceptType(int v) : value(v) {}
|
||||
NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false)
|
||||
: value(o.value) {
|
||||
++move_called;
|
||||
int value;
|
||||
int* move_called;
|
||||
constexpr NonThrowingNonNoexceptType(int v, int* mv_called) : value(v), move_called(mv_called) {}
|
||||
constexpr NonThrowingNonNoexceptType(NonThrowingNonNoexceptType&& o) noexcept(false)
|
||||
: value(o.value), move_called(o.move_called) {
|
||||
++*move_called;
|
||||
o.value = -1;
|
||||
}
|
||||
NonThrowingNonNoexceptType &
|
||||
operator=(NonThrowingNonNoexceptType &&) noexcept(false) {
|
||||
NonThrowingNonNoexceptType& operator=(NonThrowingNonNoexceptType&&) noexcept(false) {
|
||||
assert(false); // never called by the tests.
|
||||
return *this;
|
||||
}
|
||||
int value;
|
||||
};
|
||||
int NonThrowingNonNoexceptType::move_called = 0;
|
||||
|
||||
struct ThrowsOnSecondMove {
|
||||
int value;
|
||||
int move_count;
|
||||
ThrowsOnSecondMove(int v) : value(v), move_count(0) {}
|
||||
ThrowsOnSecondMove(ThrowsOnSecondMove &&o) noexcept(false)
|
||||
: value(o.value), move_count(o.move_count + 1) {
|
||||
ThrowsOnSecondMove(ThrowsOnSecondMove&& o) noexcept(false) : value(o.value), move_count(o.move_count + 1) {
|
||||
if (move_count == 2)
|
||||
do_throw<true>();
|
||||
o.value = -1;
|
||||
}
|
||||
ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) {
|
||||
ThrowsOnSecondMove& operator=(ThrowsOnSecondMove&&) {
|
||||
assert(false); // not called by test
|
||||
return *this;
|
||||
}
|
||||
@ -224,265 +204,293 @@ void test_swap_valueless_by_exception() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_swap_same_alternative() {
|
||||
TEST_CONSTEXPR_CXX20 void test_swap_same_alternative() {
|
||||
{
|
||||
using T = ThrowingTypeWithNothrowSwap;
|
||||
using V = std::variant<T, int>;
|
||||
T::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>, 100);
|
||||
using V = std::variant<ThrowingTypeWithNothrowSwap, int>;
|
||||
int move_called = 0;
|
||||
int move_assign_called = 0;
|
||||
int swap_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
|
||||
V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
|
||||
v1.swap(v2);
|
||||
assert(T::swap_called == 1);
|
||||
assert(swap_called == 1);
|
||||
assert(std::get<0>(v1).value == 100);
|
||||
assert(std::get<0>(v2).value == 42);
|
||||
swap(v1, v2);
|
||||
assert(T::swap_called == 2);
|
||||
assert(swap_called == 2);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
|
||||
assert(move_called == 0);
|
||||
assert(move_assign_called == 0);
|
||||
}
|
||||
{
|
||||
using T = NothrowMoveable;
|
||||
using V = std::variant<T, int>;
|
||||
T::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>, 100);
|
||||
using V = std::variant<NothrowMoveable, int>;
|
||||
int move_called = 0;
|
||||
int move_assign_called = 0;
|
||||
int swap_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
|
||||
V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
|
||||
v1.swap(v2);
|
||||
assert(T::swap_called == 0);
|
||||
assert(T::move_called == 1);
|
||||
assert(T::move_assign_called == 2);
|
||||
assert(swap_called == 0);
|
||||
assert(move_called == 1);
|
||||
assert(move_assign_called == 2);
|
||||
assert(std::get<0>(v1).value == 100);
|
||||
assert(std::get<0>(v2).value == 42);
|
||||
T::reset();
|
||||
|
||||
move_called = 0;
|
||||
move_assign_called = 0;
|
||||
swap_called = 0;
|
||||
|
||||
swap(v1, v2);
|
||||
assert(T::swap_called == 0);
|
||||
assert(T::move_called == 1);
|
||||
assert(T::move_assign_called == 2);
|
||||
assert(swap_called == 0);
|
||||
assert(move_called == 1);
|
||||
assert(move_assign_called == 2);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
}
|
||||
}
|
||||
|
||||
void test_swap_same_alternative_throws(){
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
{
|
||||
using T = NothrowTypeWithThrowingSwap;
|
||||
using V = std::variant<T, int>;
|
||||
T::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>, 100);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T::swap_called == 1);
|
||||
assert(T::move_called == 0);
|
||||
assert(T::move_assign_called == 0);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
{using V = std::variant<NothrowTypeWithThrowingSwap, int>;
|
||||
int move_called = 0;
|
||||
int move_assign_called = 0;
|
||||
int swap_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
|
||||
V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(swap_called == 1);
|
||||
assert(move_called == 0);
|
||||
assert(move_assign_called == 0);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
}
|
||||
|
||||
{
|
||||
using V = std::variant<ThrowingMoveCtor, int>;
|
||||
int move_called = 0;
|
||||
int move_assign_called = 0;
|
||||
int swap_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
|
||||
V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
{
|
||||
using T = ThrowingMoveCtor;
|
||||
using V = std::variant<T, int>;
|
||||
T::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>, 100);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T::move_called == 1); // call threw
|
||||
assert(T::move_assign_called == 0);
|
||||
assert(std::get<0>(v1).value ==
|
||||
42); // throw happened before v1 was moved from
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
}
|
||||
{
|
||||
using T = ThrowingMoveAssignNothrowMoveCtor;
|
||||
using V = std::variant<T, int>;
|
||||
T::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<0>, 100);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T::move_called == 1);
|
||||
assert(T::move_assign_called == 1); // call threw and didn't complete
|
||||
assert(std::get<0>(v1).value == -1); // v1 was moved from
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
assert(move_called == 1); // call threw
|
||||
assert(move_assign_called == 0);
|
||||
assert(swap_called == 0);
|
||||
assert(std::get<0>(v1).value == 42); // throw happened before v1 was moved from
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
}
|
||||
{
|
||||
using V = std::variant<ThrowingMoveAssignNothrowMoveCtor, int>;
|
||||
int move_called = 0;
|
||||
int move_assign_called = 0;
|
||||
int swap_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
|
||||
V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(move_called == 1);
|
||||
assert(move_assign_called == 1); // call threw and didn't complete
|
||||
assert(swap_called == 0);
|
||||
assert(std::get<0>(v1).value == -1); // v1 was moved from
|
||||
assert(std::get<0>(v2).value == 100);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_swap_different_alternatives() {
|
||||
TEST_CONSTEXPR_CXX20 void test_swap_different_alternatives() {
|
||||
{
|
||||
using T = NothrowMoveCtorWithThrowingSwap;
|
||||
using V = std::variant<T, int>;
|
||||
T::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
using V = std::variant<NothrowMoveCtorWithThrowingSwap, int>;
|
||||
int move_called = 0;
|
||||
int move_assign_called = 0;
|
||||
int swap_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called);
|
||||
V v2(std::in_place_index<1>, 100);
|
||||
v1.swap(v2);
|
||||
assert(T::swap_called == 0);
|
||||
assert(swap_called == 0);
|
||||
// The libc++ implementation double copies the argument, and not
|
||||
// the variant swap is called on.
|
||||
LIBCPP_ASSERT(T::move_called == 1);
|
||||
assert(T::move_called <= 2);
|
||||
assert(T::move_assign_called == 0);
|
||||
LIBCPP_ASSERT(move_called == 1);
|
||||
assert(move_called <= 2);
|
||||
assert(move_assign_called == 0);
|
||||
assert(std::get<1>(v1) == 100);
|
||||
assert(std::get<0>(v2).value == 42);
|
||||
T::reset();
|
||||
|
||||
move_called = 0;
|
||||
move_assign_called = 0;
|
||||
swap_called = 0;
|
||||
|
||||
swap(v1, v2);
|
||||
assert(T::swap_called == 0);
|
||||
LIBCPP_ASSERT(T::move_called == 2);
|
||||
assert(T::move_called <= 2);
|
||||
assert(T::move_assign_called == 0);
|
||||
assert(swap_called == 0);
|
||||
LIBCPP_ASSERT(move_called == 2);
|
||||
assert(move_called <= 2);
|
||||
assert(move_assign_called == 0);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<1>(v2) == 100);
|
||||
}
|
||||
}
|
||||
|
||||
void test_swap_different_alternatives_throws() {
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
{
|
||||
using T1 = ThrowingTypeWithNothrowSwap;
|
||||
using T2 = NonThrowingNonNoexceptType;
|
||||
using V = std::variant<T1, T2>;
|
||||
T1::reset();
|
||||
T2::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<1>, 100);
|
||||
using V = std::variant<ThrowingTypeWithNothrowSwap, NonThrowingNonNoexceptType>;
|
||||
int move_called1 = 0;
|
||||
int move_assign_called1 = 0;
|
||||
int swap_called1 = 0;
|
||||
int move_called2 = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1);
|
||||
V v2(std::in_place_index<1>, 100, &move_called2);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T1::swap_called == 0);
|
||||
assert(T1::move_called == 1); // throws
|
||||
assert(T1::move_assign_called == 0);
|
||||
assert(swap_called1 == 0);
|
||||
assert(move_called1 == 1); // throws
|
||||
assert(move_assign_called1 == 0);
|
||||
// FIXME: libc++ shouldn't move from T2 here.
|
||||
LIBCPP_ASSERT(T2::move_called == 1);
|
||||
assert(T2::move_called <= 1);
|
||||
LIBCPP_ASSERT(move_called2 == 1);
|
||||
assert(move_called2 <= 1);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
if (T2::move_called != 0)
|
||||
if (move_called2 != 0)
|
||||
assert(v2.valueless_by_exception());
|
||||
else
|
||||
assert(std::get<1>(v2).value == 100);
|
||||
}
|
||||
{
|
||||
using T1 = NonThrowingNonNoexceptType;
|
||||
using T2 = ThrowingTypeWithNothrowSwap;
|
||||
using V = std::variant<T1, T2>;
|
||||
T1::reset();
|
||||
T2::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<1>, 100);
|
||||
using V = std::variant<NonThrowingNonNoexceptType, ThrowingTypeWithNothrowSwap>;
|
||||
int move_called1 = 0;
|
||||
int move_called2 = 0;
|
||||
int move_assign_called2 = 0;
|
||||
int swap_called2 = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called1);
|
||||
V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
LIBCPP_ASSERT(T1::move_called == 0);
|
||||
assert(T1::move_called <= 1);
|
||||
assert(T2::swap_called == 0);
|
||||
assert(T2::move_called == 1); // throws
|
||||
assert(T2::move_assign_called == 0);
|
||||
if (T1::move_called != 0)
|
||||
LIBCPP_ASSERT(move_called1 == 0);
|
||||
assert(move_called1 <= 1);
|
||||
assert(swap_called2 == 0);
|
||||
assert(move_called2 == 1); // throws
|
||||
assert(move_assign_called2 == 0);
|
||||
if (move_called1 != 0)
|
||||
assert(v1.valueless_by_exception());
|
||||
else
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<1>(v2).value == 100);
|
||||
}
|
||||
// FIXME: The tests below are just very libc++ specific
|
||||
#ifdef _LIBCPP_VERSION
|
||||
# ifdef _LIBCPP_VERSION
|
||||
{
|
||||
using T1 = ThrowsOnSecondMove;
|
||||
using T2 = NonThrowingNonNoexceptType;
|
||||
using V = std::variant<T1, T2>;
|
||||
T2::reset();
|
||||
using V = std::variant<ThrowsOnSecondMove, NonThrowingNonNoexceptType>;
|
||||
int move_called = 0;
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<1>, 100);
|
||||
V v2(std::in_place_index<1>, 100, &move_called);
|
||||
v1.swap(v2);
|
||||
assert(T2::move_called == 2);
|
||||
assert(move_called == 2);
|
||||
assert(std::get<1>(v1).value == 100);
|
||||
assert(std::get<0>(v2).value == 42);
|
||||
assert(std::get<0>(v2).move_count == 1);
|
||||
}
|
||||
{
|
||||
using T1 = NonThrowingNonNoexceptType;
|
||||
using T2 = ThrowsOnSecondMove;
|
||||
using V = std::variant<T1, T2>;
|
||||
T1::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
using V = std::variant<NonThrowingNonNoexceptType, ThrowsOnSecondMove>;
|
||||
int move_called = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called);
|
||||
V v2(std::in_place_index<1>, 100);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T1::move_called == 1);
|
||||
assert(move_called == 1);
|
||||
assert(v1.valueless_by_exception());
|
||||
assert(std::get<0>(v2).value == 42);
|
||||
}
|
||||
#endif
|
||||
// testing libc++ extension. If either variant stores a nothrow move
|
||||
// constructible type v1.swap(v2) provides the strong exception safety
|
||||
// guarantee.
|
||||
#ifdef _LIBCPP_VERSION
|
||||
# endif
|
||||
// testing libc++ extension. If either variant stores a nothrow move
|
||||
// constructible type v1.swap(v2) provides the strong exception safety
|
||||
// guarantee.
|
||||
# ifdef _LIBCPP_VERSION
|
||||
{
|
||||
|
||||
using T1 = ThrowingTypeWithNothrowSwap;
|
||||
using T2 = NothrowMoveable;
|
||||
using V = std::variant<T1, T2>;
|
||||
T1::reset();
|
||||
T2::reset();
|
||||
V v1(std::in_place_index<0>, 42);
|
||||
V v2(std::in_place_index<1>, 100);
|
||||
using V = std::variant<ThrowingTypeWithNothrowSwap, NothrowMoveable>;
|
||||
int move_called1 = 0;
|
||||
int move_assign_called1 = 0;
|
||||
int swap_called1 = 0;
|
||||
int move_called2 = 0;
|
||||
int move_assign_called2 = 0;
|
||||
int swap_called2 = 0;
|
||||
V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1);
|
||||
V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2);
|
||||
try {
|
||||
v1.swap(v2);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T1::swap_called == 0);
|
||||
assert(T1::move_called == 1);
|
||||
assert(T1::move_assign_called == 0);
|
||||
assert(T2::swap_called == 0);
|
||||
assert(T2::move_called == 2);
|
||||
assert(T2::move_assign_called == 0);
|
||||
assert(swap_called1 == 0);
|
||||
assert(move_called1 == 1);
|
||||
assert(move_assign_called1 == 0);
|
||||
assert(swap_called2 == 0);
|
||||
assert(move_called2 == 2);
|
||||
assert(move_assign_called2 == 0);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<1>(v2).value == 100);
|
||||
// swap again, but call v2's swap.
|
||||
T1::reset();
|
||||
T2::reset();
|
||||
|
||||
move_called1 = 0;
|
||||
move_assign_called1 = 0;
|
||||
swap_called1 = 0;
|
||||
move_called2 = 0;
|
||||
move_assign_called2 = 0;
|
||||
swap_called2 = 0;
|
||||
|
||||
try {
|
||||
v2.swap(v1);
|
||||
assert(false);
|
||||
} catch (int) {
|
||||
}
|
||||
assert(T1::swap_called == 0);
|
||||
assert(T1::move_called == 1);
|
||||
assert(T1::move_assign_called == 0);
|
||||
assert(T2::swap_called == 0);
|
||||
assert(T2::move_called == 2);
|
||||
assert(T2::move_assign_called == 0);
|
||||
assert(swap_called1 == 0);
|
||||
assert(move_called1 == 1);
|
||||
assert(move_assign_called1 == 0);
|
||||
assert(swap_called2 == 0);
|
||||
assert(move_called2 == 2);
|
||||
assert(move_assign_called2 == 0);
|
||||
assert(std::get<0>(v1).value == 42);
|
||||
assert(std::get<1>(v2).value == 100);
|
||||
}
|
||||
#endif // _LIBCPP_VERSION
|
||||
# endif // _LIBCPP_VERSION
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class Var>
|
||||
constexpr auto has_swap_member_imp(int)
|
||||
-> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) {
|
||||
constexpr auto has_swap_member_imp(int) -> decltype(std::declval<Var&>().swap(std::declval<Var&>()), true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Var> constexpr auto has_swap_member_imp(long) -> bool {
|
||||
template <class Var>
|
||||
constexpr auto has_swap_member_imp(long) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Var> constexpr bool has_swap_member() {
|
||||
template <class Var>
|
||||
constexpr bool has_swap_member() {
|
||||
return has_swap_member_imp<Var>(0);
|
||||
}
|
||||
|
||||
void test_swap_sfinae() {
|
||||
constexpr void test_swap_sfinae() {
|
||||
{
|
||||
// This variant type does not provide either a member or non-member swap
|
||||
// but is still swappable via the generic swap algorithm, since the
|
||||
@ -508,7 +516,7 @@ void test_swap_sfinae() {
|
||||
}
|
||||
}
|
||||
|
||||
void test_swap_noexcept() {
|
||||
_LIBCPP_CONSTEXPR_SINCE_CXX20 void test_swap_noexcept() {
|
||||
{
|
||||
using V = std::variant<int, NothrowMoveable>;
|
||||
static_assert(std::is_swappable_v<V> && has_swap_member<V>(), "");
|
||||
@ -581,12 +589,28 @@ void test_swap_noexcept() {
|
||||
template class std::variant<int, NotSwappable>;
|
||||
#endif
|
||||
|
||||
int main(int, char**) {
|
||||
void non_constexpr_test() {
|
||||
test_swap_valueless_by_exception();
|
||||
test_swap_same_alternative_throws();
|
||||
test_swap_different_alternatives_throws();
|
||||
}
|
||||
|
||||
TEST_CONSTEXPR_CXX20 bool test() {
|
||||
test_swap_same_alternative();
|
||||
test_swap_different_alternatives();
|
||||
test_swap_sfinae();
|
||||
test_swap_noexcept();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
non_constexpr_test();
|
||||
test();
|
||||
|
||||
#if TEST_STD_VER >= 20
|
||||
static_assert(test());
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user