diff --git a/include/memory b/include/memory index df35a0714..790a161cc 100644 --- a/include/memory +++ b/include/memory @@ -5574,6 +5574,15 @@ template _LIBCPP_INLINE_VISIBILITY void __swap_allocator(_Alloc &, _Alloc &, false_type) _NOEXCEPT {} +template > +struct __noexcept_move_assign_container : public integral_constant 14 + || _Traits::is_always_equal::value +#else + && is_nothrow_move_assignable<_Alloc>::value +#endif + > {}; _LIBCPP_END_NAMESPACE_STD diff --git a/include/string b/include/string index 2c677ce4b..372522cea 100644 --- a/include/string +++ b/include/string @@ -115,8 +115,8 @@ public: basic_string& operator=(const basic_string& str); basic_string& operator=(basic_string&& str) noexcept( - allocator_type::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + allocator_type::propagate_on_container_move_assignment::value || + allocator_type::is_always_equal::value ); // C++17 basic_string& operator=(const value_type* s); basic_string& operator=(value_type c); basic_string& operator=(initializer_list); @@ -1377,8 +1377,7 @@ public: #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES _LIBCPP_INLINE_VISIBILITY basic_string& operator=(basic_string&& __str) - _NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)); #endif _LIBCPP_INLINE_VISIBILITY basic_string& operator=(const value_type* __s) {return assign(__s);} basic_string& operator=(value_type __c); @@ -1845,10 +1844,15 @@ private: #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES _LIBCPP_INLINE_VISIBILITY - void __move_assign(basic_string& __str, false_type); + void __move_assign(basic_string& __str, false_type) + _NOEXCEPT_(__alloc_traits::is_always_equal::value); _LIBCPP_INLINE_VISIBILITY void __move_assign(basic_string& __str, true_type) +#if _LIBCPP_STD_VER > 14 + _NOEXCEPT; +#else _NOEXCEPT_(is_nothrow_move_assignable::value); +#endif #endif _LIBCPP_INLINE_VISIBILITY @@ -2430,6 +2434,7 @@ template inline _LIBCPP_INLINE_VISIBILITY void basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, false_type) + _NOEXCEPT_(__alloc_traits::is_always_equal::value) { if (__alloc() != __str.__alloc()) assign(__str); @@ -2441,7 +2446,11 @@ template inline _LIBCPP_INLINE_VISIBILITY void basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, true_type) +#if _LIBCPP_STD_VER > 14 + _NOEXCEPT +#else _NOEXCEPT_(is_nothrow_move_assignable::value) +#endif { clear(); shrink_to_fit(); @@ -2454,8 +2463,7 @@ template inline _LIBCPP_INLINE_VISIBILITY basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::operator=(basic_string&& __str) - _NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value) + _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)) { __move_assign(__str, integral_constant()); diff --git a/include/vector b/include/vector index 049d3c870..1cafae16e 100644 --- a/include/vector +++ b/include/vector @@ -51,8 +51,8 @@ public: vector& operator=(const vector& x); vector& operator=(vector&& x) noexcept( - allocator_type::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + allocator_type::propagate_on_container_move_assignment::value || + allocator_type::is_always_equal::value); // C++17 vector& operator=(initializer_list il); template void assign(InputIterator first, InputIterator last); @@ -175,8 +175,8 @@ public: vector& operator=(const vector& x); vector& operator=(vector&& x) noexcept( - allocator_type::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + allocator_type::propagate_on_container_move_assignment::value || + allocator_type::is_always_equal::value); // C++17 vector& operator=(initializer_list il); template void assign(InputIterator first, InputIterator last); @@ -562,9 +562,7 @@ public: vector(vector&& __x, const allocator_type& __a); _LIBCPP_INLINE_VISIBILITY vector& operator=(vector&& __x) - _NOEXCEPT_( - __alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)); #endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES #ifndef _LIBCPP_HAS_NO_GENERALIZED_INITIALIZERS _LIBCPP_INLINE_VISIBILITY @@ -787,7 +785,8 @@ private: void __move_range(pointer __from_s, pointer __from_e, pointer __to); void __move_assign(vector& __c, true_type) _NOEXCEPT_(is_nothrow_move_assignable::value); - void __move_assign(vector& __c, false_type); + void __move_assign(vector& __c, false_type) + _NOEXCEPT_(__alloc_traits::is_always_equal::value); _LIBCPP_INLINE_VISIBILITY void __destruct_at_end(pointer __new_last) _NOEXCEPT { @@ -1303,9 +1302,7 @@ template inline _LIBCPP_INLINE_VISIBILITY vector<_Tp, _Allocator>& vector<_Tp, _Allocator>::operator=(vector&& __x) - _NOEXCEPT_( - __alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value) + _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)) { __move_assign(__x, integral_constant()); @@ -1315,6 +1312,7 @@ vector<_Tp, _Allocator>::operator=(vector&& __x) template void vector<_Tp, _Allocator>::__move_assign(vector& __c, false_type) + _NOEXCEPT_(__alloc_traits::is_always_equal::value) { if (__base::__alloc() != __c.__alloc()) { @@ -2213,9 +2211,7 @@ public: vector(vector&& __v, const allocator_type& __a); _LIBCPP_INLINE_VISIBILITY vector& operator=(vector&& __v) - _NOEXCEPT_( - __alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value); + _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)); #endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES #ifndef _LIBCPP_HAS_NO_GENERALIZED_INITIALIZERS _LIBCPP_INLINE_VISIBILITY @@ -2838,9 +2834,7 @@ template inline _LIBCPP_INLINE_VISIBILITY vector& vector::operator=(vector&& __v) - _NOEXCEPT_( - __alloc_traits::propagate_on_container_move_assignment::value && - is_nothrow_move_assignable::value) + _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value)) { __move_assign(__v, integral_constant()); diff --git a/test/std/containers/sequences/vector.bool/move_assign_noexcept.pass.cpp b/test/std/containers/sequences/vector.bool/move_assign_noexcept.pass.cpp index b580eb4ae..4dd871c94 100644 --- a/test/std/containers/sequences/vector.bool/move_assign_noexcept.pass.cpp +++ b/test/std/containers/sequences/vector.bool/move_assign_noexcept.pass.cpp @@ -28,6 +28,32 @@ struct some_alloc some_alloc(const some_alloc&); }; +template +struct some_alloc2 +{ + typedef T value_type; + + some_alloc2() {} + some_alloc2(const some_alloc2&); + void deallocate(void*, unsigned) {} + + typedef std::false_type propagate_on_container_move_assignment; + typedef std::true_type is_always_equal; +}; + +template +struct some_alloc3 +{ + typedef T value_type; + + some_alloc3() {} + some_alloc3(const some_alloc3&); + void deallocate(void*, unsigned) {} + + typedef std::false_type propagate_on_container_move_assignment; + typedef std::false_type is_always_equal; +}; + int main() { #if __has_feature(cxx_noexcept) @@ -45,7 +71,22 @@ int main() } { typedef std::vector> C; +#if TEST_STD_VER > 14 + static_assert( std::is_nothrow_move_assignable::value, ""); +#else + static_assert(!std::is_nothrow_move_assignable::value, ""); +#endif + } +#if TEST_STD_VER > 14 + { // POCMA false, is_always_equal true + typedef std::vector> C; + static_assert( std::is_nothrow_move_assignable::value, ""); + } + { // POCMA false, is_always_equal false + typedef std::vector> C; static_assert(!std::is_nothrow_move_assignable::value, ""); } #endif + +#endif } diff --git a/test/std/containers/sequences/vector/vector.cons/move_assign_noexcept.pass.cpp b/test/std/containers/sequences/vector/vector.cons/move_assign_noexcept.pass.cpp index 1c4a4f7c9..c09224497 100644 --- a/test/std/containers/sequences/vector/vector.cons/move_assign_noexcept.pass.cpp +++ b/test/std/containers/sequences/vector/vector.cons/move_assign_noexcept.pass.cpp @@ -29,6 +29,33 @@ struct some_alloc some_alloc(const some_alloc&); }; +template +struct some_alloc2 +{ + typedef T value_type; + + some_alloc2() {} + some_alloc2(const some_alloc2&); + void deallocate(void*, unsigned) {} + + typedef std::false_type propagate_on_container_move_assignment; + typedef std::true_type is_always_equal; +}; + +template +struct some_alloc3 +{ + typedef T value_type; + + some_alloc3() {} + some_alloc3(const some_alloc3&); + void deallocate(void*, unsigned) {} + + typedef std::false_type propagate_on_container_move_assignment; + typedef std::false_type is_always_equal; +}; + + int main() { #if __has_feature(cxx_noexcept) @@ -46,7 +73,24 @@ int main() } { typedef std::vector> C; + // In C++17, move assignment for allocators are not allowed to throw +#if TEST_STD_VER > 14 + static_assert( std::is_nothrow_move_assignable::value, ""); +#else + static_assert(!std::is_nothrow_move_assignable::value, ""); +#endif + } + +#if TEST_STD_VER > 14 + { // POCMA false, is_always_equal true + typedef std::vector> C; + static_assert( std::is_nothrow_move_assignable::value, ""); + } + { // POCMA false, is_always_equal false + typedef std::vector> C; static_assert(!std::is_nothrow_move_assignable::value, ""); } #endif + +#endif } diff --git a/test/std/strings/basic.string/string.cons/move_assign_noexcept.pass.cpp b/test/std/strings/basic.string/string.cons/move_assign_noexcept.pass.cpp index 32c9514a9..d7bd5e06b 100644 --- a/test/std/strings/basic.string/string.cons/move_assign_noexcept.pass.cpp +++ b/test/std/strings/basic.string/string.cons/move_assign_noexcept.pass.cpp @@ -11,14 +11,18 @@ // basic_string& operator=(basic_string&& c) // noexcept( -// allocator_type::propagate_on_container_move_assignment::value && -// is_nothrow_move_assignable::value); - -// This tests a conforming extension +// allocator_traits::propagate_on_container_move_assignment::value || +// allocator_traits::is_always_equal::value); // C++17 +// +// before C++17, we use the conforming extension +// noexcept( +// allocator_type::propagate_on_container_move_assignment::value && +// is_nothrow_move_assignable::value); #include #include +#include "test_macros.h" #include "test_allocator.h" template @@ -28,6 +32,32 @@ struct some_alloc some_alloc(const some_alloc&); }; +template +struct some_alloc2 +{ + typedef T value_type; + + some_alloc2() {} + some_alloc2(const some_alloc2&); + void deallocate(void*, unsigned) {} + + typedef std::false_type propagate_on_container_move_assignment; + typedef std::true_type is_always_equal; +}; + +template +struct some_alloc3 +{ + typedef T value_type; + + some_alloc3() {} + some_alloc3(const some_alloc3&); + void deallocate(void*, unsigned) {} + + typedef std::false_type propagate_on_container_move_assignment; + typedef std::false_type is_always_equal; +}; + int main() { #if __has_feature(cxx_noexcept) @@ -41,7 +71,25 @@ int main() } { typedef std::basic_string, some_alloc> C; +#if TEST_STD_VER > 14 + // if the allocators are always equal, then the move assignment can be noexcept + static_assert( std::is_nothrow_move_assignable::value, ""); +#else + static_assert(!std::is_nothrow_move_assignable::value, ""); +#endif + } +#if TEST_STD_VER > 14 + { + // POCMA is false, always equal + typedef std::basic_string, some_alloc2> C; + static_assert( std::is_nothrow_move_assignable::value, ""); + } + { + // POCMA is false, not always equal + typedef std::basic_string, some_alloc3> C; static_assert(!std::is_nothrow_move_assignable::value, ""); } #endif + +#endif }