implement more of N4258 - Cleaning up noexcept in the standard library. Specifically add new noexcept stuff to vector and string's move-assignment operations
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@245330 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/memory b/include/memory
index df35a07..790a161 100644
--- a/include/memory
+++ b/include/memory
@@ -5574,6 +5574,15 @@
_LIBCPP_INLINE_VISIBILITY
void __swap_allocator(_Alloc &, _Alloc &, false_type) _NOEXCEPT {}
+template <typename _Alloc, typename _Traits=allocator_traits<_Alloc>>
+struct __noexcept_move_assign_container : public integral_constant<bool,
+ _Traits::propagate_on_container_move_assignment::value
+#if _LIBCPP_STD_VER > 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 2c677ce..372522c 100644
--- a/include/string
+++ b/include/string
@@ -115,8 +115,8 @@
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<allocator_type>::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<value_type>);
@@ -1377,8 +1377,7 @@
#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<allocator_type>::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,11 +1844,16 @@
#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<allocator_type>::value);
#endif
+#endif
_LIBCPP_INLINE_VISIBILITY
void
@@ -2430,6 +2434,7 @@
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 @@
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<allocator_type>::value)
+#endif
{
clear();
shrink_to_fit();
@@ -2454,8 +2463,7 @@
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<allocator_type>::value)
+ _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
{
__move_assign(__str, integral_constant<bool,
__alloc_traits::propagate_on_container_move_assignment::value>());
diff --git a/include/vector b/include/vector
index 049d3c8..1cafae1 100644
--- a/include/vector
+++ b/include/vector
@@ -51,8 +51,8 @@
vector& operator=(const vector& x);
vector& operator=(vector&& x)
noexcept(
- allocator_type::propagate_on_container_move_assignment::value &&
- is_nothrow_move_assignable<allocator_type>::value);
+ allocator_type::propagate_on_container_move_assignment::value ||
+ allocator_type::is_always_equal::value); // C++17
vector& operator=(initializer_list<value_type> il);
template <class InputIterator>
void assign(InputIterator first, InputIterator last);
@@ -175,8 +175,8 @@
vector& operator=(const vector& x);
vector& operator=(vector&& x)
noexcept(
- allocator_type::propagate_on_container_move_assignment::value &&
- is_nothrow_move_assignable<allocator_type>::value);
+ allocator_type::propagate_on_container_move_assignment::value ||
+ allocator_type::is_always_equal::value); // C++17
vector& operator=(initializer_list<value_type> il);
template <class InputIterator>
void assign(InputIterator first, InputIterator last);
@@ -562,9 +562,7 @@
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<allocator_type>::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 @@
void __move_range(pointer __from_s, pointer __from_e, pointer __to);
void __move_assign(vector& __c, true_type)
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::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 @@
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<allocator_type>::value)
+ _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
{
__move_assign(__x, integral_constant<bool,
__alloc_traits::propagate_on_container_move_assignment::value>());
@@ -1315,6 +1312,7 @@
template <class _Tp, class _Allocator>
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 @@
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<allocator_type>::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 @@
inline _LIBCPP_INLINE_VISIBILITY
vector<bool, _Allocator>&
vector<bool, _Allocator>::operator=(vector&& __v)
- _NOEXCEPT_(
- __alloc_traits::propagate_on_container_move_assignment::value &&
- is_nothrow_move_assignable<allocator_type>::value)
+ _NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
{
__move_assign(__v, integral_constant<bool,
__storage_traits::propagate_on_container_move_assignment::value>());
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 b580eb4..4dd871c 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 @@
some_alloc(const some_alloc&);
};
+template <class T>
+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 <class T>
+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 @@
}
{
typedef std::vector<bool, some_alloc<bool>> C;
+#if TEST_STD_VER > 14
+ static_assert( std::is_nothrow_move_assignable<C>::value, "");
+#else
+ static_assert(!std::is_nothrow_move_assignable<C>::value, "");
+#endif
+ }
+#if TEST_STD_VER > 14
+ { // POCMA false, is_always_equal true
+ typedef std::vector<bool, some_alloc2<bool>> C;
+ static_assert( std::is_nothrow_move_assignable<C>::value, "");
+ }
+ { // POCMA false, is_always_equal false
+ typedef std::vector<bool, some_alloc3<bool>> C;
static_assert(!std::is_nothrow_move_assignable<C>::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 1c4a4f7..c092244 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 @@
some_alloc(const some_alloc&);
};
+template <class T>
+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 <class T>
+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 @@
}
{
typedef std::vector<MoveOnly, some_alloc<MoveOnly>> 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<C>::value, "");
+#else
+ static_assert(!std::is_nothrow_move_assignable<C>::value, "");
+#endif
+ }
+
+#if TEST_STD_VER > 14
+ { // POCMA false, is_always_equal true
+ typedef std::vector<MoveOnly, some_alloc2<MoveOnly>> C;
+ static_assert( std::is_nothrow_move_assignable<C>::value, "");
+ }
+ { // POCMA false, is_always_equal false
+ typedef std::vector<MoveOnly, some_alloc3<MoveOnly>> C;
static_assert(!std::is_nothrow_move_assignable<C>::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 32c9514..d7bd5e0 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<allocator_type>::value);
-
-// This tests a conforming extension
+// allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
+// allocator_traits<allocator_type>::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<allocator_type>::value);
#include <string>
#include <cassert>
+#include "test_macros.h"
#include "test_allocator.h"
template <class T>
@@ -28,6 +32,32 @@
some_alloc(const some_alloc&);
};
+template <class T>
+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 <class T>
+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 @@
}
{
typedef std::basic_string<char, std::char_traits<char>, some_alloc<char>> 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<C>::value, "");
+#else
+ static_assert(!std::is_nothrow_move_assignable<C>::value, "");
+#endif
+ }
+#if TEST_STD_VER > 14
+ {
+ // POCMA is false, always equal
+ typedef std::basic_string<char, std::char_traits<char>, some_alloc2<char>> C;
+ static_assert( std::is_nothrow_move_assignable<C>::value, "");
+ }
+ {
+ // POCMA is false, not always equal
+ typedef std::basic_string<char, std::char_traits<char>, some_alloc3<char>> C;
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
}
#endif
+
+#endif
}