[libc++] Make constexpr std::variant. Implement P2231R1 (#83335)

Fixes #86686
This commit is contained in:
Hui 2024-05-10 15:13:00 +01:00 committed by GitHub
parent 331f22af4b
commit 52271a5c11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1321 additions and 835 deletions

View File

@ -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
-----------------------------

View File

@ -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:

View File

@ -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.

View File

@ -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);

View File

@ -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;
}

View File

@ -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, &copy_construct, &copy_assign, &move_construct, &move_assign);
V v2(std::in_place_type<CopyAssign>, 42, &alive, &copy_construct, &copy_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, &copy_construct, &copy_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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}