Skip to content

Commit 1378a5a

Browse files
committed
implement more of N4258 - Cleaning up noexcept in the standard library. Specifically add new noexcept stuff to vector and string's move-assignment operations
llvm-svn: 245330
1 parent d55bcf2 commit 1378a5a

File tree

6 files changed

+172
-28
lines changed

6 files changed

+172
-28
lines changed

libcxx/include/memory

+9
Original file line numberDiff line numberDiff line change
@@ -5574,6 +5574,15 @@ template <typename _Alloc>
55745574
_LIBCPP_INLINE_VISIBILITY
55755575
void __swap_allocator(_Alloc &, _Alloc &, false_type) _NOEXCEPT {}
55765576

5577+
template <typename _Alloc, typename _Traits=allocator_traits<_Alloc>>
5578+
struct __noexcept_move_assign_container : public integral_constant<bool,
5579+
_Traits::propagate_on_container_move_assignment::value
5580+
#if _LIBCPP_STD_VER > 14
5581+
|| _Traits::is_always_equal::value
5582+
#else
5583+
&& is_nothrow_move_assignable<_Alloc>::value
5584+
#endif
5585+
> {};
55775586

55785587
_LIBCPP_END_NAMESPACE_STD
55795588

libcxx/include/string

+15-7
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ public:
115115
basic_string& operator=(const basic_string& str);
116116
basic_string& operator=(basic_string&& str)
117117
noexcept(
118-
allocator_type::propagate_on_container_move_assignment::value &&
119-
is_nothrow_move_assignable<allocator_type>::value);
118+
allocator_type::propagate_on_container_move_assignment::value ||
119+
allocator_type::is_always_equal::value ); // C++17
120120
basic_string& operator=(const value_type* s);
121121
basic_string& operator=(value_type c);
122122
basic_string& operator=(initializer_list<value_type>);
@@ -1377,8 +1377,7 @@ public:
13771377
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
13781378
_LIBCPP_INLINE_VISIBILITY
13791379
basic_string& operator=(basic_string&& __str)
1380-
_NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value &&
1381-
is_nothrow_move_assignable<allocator_type>::value);
1380+
_NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value));
13821381
#endif
13831382
_LIBCPP_INLINE_VISIBILITY basic_string& operator=(const value_type* __s) {return assign(__s);}
13841383
basic_string& operator=(value_type __c);
@@ -1845,10 +1844,15 @@ private:
18451844

18461845
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
18471846
_LIBCPP_INLINE_VISIBILITY
1848-
void __move_assign(basic_string& __str, false_type);
1847+
void __move_assign(basic_string& __str, false_type)
1848+
_NOEXCEPT_(__alloc_traits::is_always_equal::value);
18491849
_LIBCPP_INLINE_VISIBILITY
18501850
void __move_assign(basic_string& __str, true_type)
1851+
#if _LIBCPP_STD_VER > 14
1852+
_NOEXCEPT;
1853+
#else
18511854
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
1855+
#endif
18521856
#endif
18531857

18541858
_LIBCPP_INLINE_VISIBILITY
@@ -2430,6 +2434,7 @@ template <class _CharT, class _Traits, class _Allocator>
24302434
inline _LIBCPP_INLINE_VISIBILITY
24312435
void
24322436
basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, false_type)
2437+
_NOEXCEPT_(__alloc_traits::is_always_equal::value)
24332438
{
24342439
if (__alloc() != __str.__alloc())
24352440
assign(__str);
@@ -2441,7 +2446,11 @@ template <class _CharT, class _Traits, class _Allocator>
24412446
inline _LIBCPP_INLINE_VISIBILITY
24422447
void
24432448
basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, true_type)
2449+
#if _LIBCPP_STD_VER > 14
2450+
_NOEXCEPT
2451+
#else
24442452
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value)
2453+
#endif
24452454
{
24462455
clear();
24472456
shrink_to_fit();
@@ -2454,8 +2463,7 @@ template <class _CharT, class _Traits, class _Allocator>
24542463
inline _LIBCPP_INLINE_VISIBILITY
24552464
basic_string<_CharT, _Traits, _Allocator>&
24562465
basic_string<_CharT, _Traits, _Allocator>::operator=(basic_string&& __str)
2457-
_NOEXCEPT_(__alloc_traits::propagate_on_container_move_assignment::value &&
2458-
is_nothrow_move_assignable<allocator_type>::value)
2466+
_NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
24592467
{
24602468
__move_assign(__str, integral_constant<bool,
24612469
__alloc_traits::propagate_on_container_move_assignment::value>());

libcxx/include/vector

+11-17
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public:
5151
vector& operator=(const vector& x);
5252
vector& operator=(vector&& x)
5353
noexcept(
54-
allocator_type::propagate_on_container_move_assignment::value &&
55-
is_nothrow_move_assignable<allocator_type>::value);
54+
allocator_type::propagate_on_container_move_assignment::value ||
55+
allocator_type::is_always_equal::value); // C++17
5656
vector& operator=(initializer_list<value_type> il);
5757
template <class InputIterator>
5858
void assign(InputIterator first, InputIterator last);
@@ -175,8 +175,8 @@ public:
175175
vector& operator=(const vector& x);
176176
vector& operator=(vector&& x)
177177
noexcept(
178-
allocator_type::propagate_on_container_move_assignment::value &&
179-
is_nothrow_move_assignable<allocator_type>::value);
178+
allocator_type::propagate_on_container_move_assignment::value ||
179+
allocator_type::is_always_equal::value); // C++17
180180
vector& operator=(initializer_list<value_type> il);
181181
template <class InputIterator>
182182
void assign(InputIterator first, InputIterator last);
@@ -562,9 +562,7 @@ public:
562562
vector(vector&& __x, const allocator_type& __a);
563563
_LIBCPP_INLINE_VISIBILITY
564564
vector& operator=(vector&& __x)
565-
_NOEXCEPT_(
566-
__alloc_traits::propagate_on_container_move_assignment::value &&
567-
is_nothrow_move_assignable<allocator_type>::value);
565+
_NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value));
568566
#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
569567
#ifndef _LIBCPP_HAS_NO_GENERALIZED_INITIALIZERS
570568
_LIBCPP_INLINE_VISIBILITY
@@ -787,7 +785,8 @@ private:
787785
void __move_range(pointer __from_s, pointer __from_e, pointer __to);
788786
void __move_assign(vector& __c, true_type)
789787
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value);
790-
void __move_assign(vector& __c, false_type);
788+
void __move_assign(vector& __c, false_type)
789+
_NOEXCEPT_(__alloc_traits::is_always_equal::value);
791790
_LIBCPP_INLINE_VISIBILITY
792791
void __destruct_at_end(pointer __new_last) _NOEXCEPT
793792
{
@@ -1303,9 +1302,7 @@ template <class _Tp, class _Allocator>
13031302
inline _LIBCPP_INLINE_VISIBILITY
13041303
vector<_Tp, _Allocator>&
13051304
vector<_Tp, _Allocator>::operator=(vector&& __x)
1306-
_NOEXCEPT_(
1307-
__alloc_traits::propagate_on_container_move_assignment::value &&
1308-
is_nothrow_move_assignable<allocator_type>::value)
1305+
_NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
13091306
{
13101307
__move_assign(__x, integral_constant<bool,
13111308
__alloc_traits::propagate_on_container_move_assignment::value>());
@@ -1315,6 +1312,7 @@ vector<_Tp, _Allocator>::operator=(vector&& __x)
13151312
template <class _Tp, class _Allocator>
13161313
void
13171314
vector<_Tp, _Allocator>::__move_assign(vector& __c, false_type)
1315+
_NOEXCEPT_(__alloc_traits::is_always_equal::value)
13181316
{
13191317
if (__base::__alloc() != __c.__alloc())
13201318
{
@@ -2213,9 +2211,7 @@ public:
22132211
vector(vector&& __v, const allocator_type& __a);
22142212
_LIBCPP_INLINE_VISIBILITY
22152213
vector& operator=(vector&& __v)
2216-
_NOEXCEPT_(
2217-
__alloc_traits::propagate_on_container_move_assignment::value &&
2218-
is_nothrow_move_assignable<allocator_type>::value);
2214+
_NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value));
22192215
#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
22202216
#ifndef _LIBCPP_HAS_NO_GENERALIZED_INITIALIZERS
22212217
_LIBCPP_INLINE_VISIBILITY
@@ -2838,9 +2834,7 @@ template <class _Allocator>
28382834
inline _LIBCPP_INLINE_VISIBILITY
28392835
vector<bool, _Allocator>&
28402836
vector<bool, _Allocator>::operator=(vector&& __v)
2841-
_NOEXCEPT_(
2842-
__alloc_traits::propagate_on_container_move_assignment::value &&
2843-
is_nothrow_move_assignable<allocator_type>::value)
2837+
_NOEXCEPT_((__noexcept_move_assign_container<_Allocator, __alloc_traits>::value))
28442838
{
28452839
__move_assign(__v, integral_constant<bool,
28462840
__storage_traits::propagate_on_container_move_assignment::value>());

libcxx/test/std/containers/sequences/vector.bool/move_assign_noexcept.pass.cpp

+41
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,32 @@ struct some_alloc
2828
some_alloc(const some_alloc&);
2929
};
3030

31+
template <class T>
32+
struct some_alloc2
33+
{
34+
typedef T value_type;
35+
36+
some_alloc2() {}
37+
some_alloc2(const some_alloc2&);
38+
void deallocate(void*, unsigned) {}
39+
40+
typedef std::false_type propagate_on_container_move_assignment;
41+
typedef std::true_type is_always_equal;
42+
};
43+
44+
template <class T>
45+
struct some_alloc3
46+
{
47+
typedef T value_type;
48+
49+
some_alloc3() {}
50+
some_alloc3(const some_alloc3&);
51+
void deallocate(void*, unsigned) {}
52+
53+
typedef std::false_type propagate_on_container_move_assignment;
54+
typedef std::false_type is_always_equal;
55+
};
56+
3157
int main()
3258
{
3359
#if __has_feature(cxx_noexcept)
@@ -45,7 +71,22 @@ int main()
4571
}
4672
{
4773
typedef std::vector<bool, some_alloc<bool>> C;
74+
#if TEST_STD_VER > 14
75+
static_assert( std::is_nothrow_move_assignable<C>::value, "");
76+
#else
4877
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
78+
#endif
79+
}
80+
#if TEST_STD_VER > 14
81+
{ // POCMA false, is_always_equal true
82+
typedef std::vector<bool, some_alloc2<bool>> C;
83+
static_assert( std::is_nothrow_move_assignable<C>::value, "");
4984
}
85+
{ // POCMA false, is_always_equal false
86+
typedef std::vector<bool, some_alloc3<bool>> C;
87+
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
88+
}
89+
#endif
90+
5091
#endif
5192
}

libcxx/test/std/containers/sequences/vector/vector.cons/move_assign_noexcept.pass.cpp

+44
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,33 @@ struct some_alloc
2929
some_alloc(const some_alloc&);
3030
};
3131

32+
template <class T>
33+
struct some_alloc2
34+
{
35+
typedef T value_type;
36+
37+
some_alloc2() {}
38+
some_alloc2(const some_alloc2&);
39+
void deallocate(void*, unsigned) {}
40+
41+
typedef std::false_type propagate_on_container_move_assignment;
42+
typedef std::true_type is_always_equal;
43+
};
44+
45+
template <class T>
46+
struct some_alloc3
47+
{
48+
typedef T value_type;
49+
50+
some_alloc3() {}
51+
some_alloc3(const some_alloc3&);
52+
void deallocate(void*, unsigned) {}
53+
54+
typedef std::false_type propagate_on_container_move_assignment;
55+
typedef std::false_type is_always_equal;
56+
};
57+
58+
3259
int main()
3360
{
3461
#if __has_feature(cxx_noexcept)
@@ -46,7 +73,24 @@ int main()
4673
}
4774
{
4875
typedef std::vector<MoveOnly, some_alloc<MoveOnly>> C;
76+
// In C++17, move assignment for allocators are not allowed to throw
77+
#if TEST_STD_VER > 14
78+
static_assert( std::is_nothrow_move_assignable<C>::value, "");
79+
#else
80+
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
81+
#endif
82+
}
83+
84+
#if TEST_STD_VER > 14
85+
{ // POCMA false, is_always_equal true
86+
typedef std::vector<MoveOnly, some_alloc2<MoveOnly>> C;
87+
static_assert( std::is_nothrow_move_assignable<C>::value, "");
88+
}
89+
{ // POCMA false, is_always_equal false
90+
typedef std::vector<MoveOnly, some_alloc3<MoveOnly>> C;
4991
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
5092
}
5193
#endif
94+
95+
#endif
5296
}

libcxx/test/std/strings/basic.string/string.cons/move_assign_noexcept.pass.cpp

+52-4
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111

1212
// basic_string& operator=(basic_string&& c)
1313
// noexcept(
14-
// allocator_type::propagate_on_container_move_assignment::value &&
15-
// is_nothrow_move_assignable<allocator_type>::value);
16-
17-
// This tests a conforming extension
14+
// allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
15+
// allocator_traits<allocator_type>::is_always_equal::value); // C++17
16+
//
17+
// before C++17, we use the conforming extension
18+
// noexcept(
19+
// allocator_type::propagate_on_container_move_assignment::value &&
20+
// is_nothrow_move_assignable<allocator_type>::value);
1821

1922
#include <string>
2023
#include <cassert>
2124

25+
#include "test_macros.h"
2226
#include "test_allocator.h"
2327

2428
template <class T>
@@ -28,6 +32,32 @@ struct some_alloc
2832
some_alloc(const some_alloc&);
2933
};
3034

35+
template <class T>
36+
struct some_alloc2
37+
{
38+
typedef T value_type;
39+
40+
some_alloc2() {}
41+
some_alloc2(const some_alloc2&);
42+
void deallocate(void*, unsigned) {}
43+
44+
typedef std::false_type propagate_on_container_move_assignment;
45+
typedef std::true_type is_always_equal;
46+
};
47+
48+
template <class T>
49+
struct some_alloc3
50+
{
51+
typedef T value_type;
52+
53+
some_alloc3() {}
54+
some_alloc3(const some_alloc3&);
55+
void deallocate(void*, unsigned) {}
56+
57+
typedef std::false_type propagate_on_container_move_assignment;
58+
typedef std::false_type is_always_equal;
59+
};
60+
3161
int main()
3262
{
3363
#if __has_feature(cxx_noexcept)
@@ -41,7 +71,25 @@ int main()
4171
}
4272
{
4373
typedef std::basic_string<char, std::char_traits<char>, some_alloc<char>> C;
74+
#if TEST_STD_VER > 14
75+
// if the allocators are always equal, then the move assignment can be noexcept
76+
static_assert( std::is_nothrow_move_assignable<C>::value, "");
77+
#else
4478
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
79+
#endif
80+
}
81+
#if TEST_STD_VER > 14
82+
{
83+
// POCMA is false, always equal
84+
typedef std::basic_string<char, std::char_traits<char>, some_alloc2<char>> C;
85+
static_assert( std::is_nothrow_move_assignable<C>::value, "");
4586
}
87+
{
88+
// POCMA is false, not always equal
89+
typedef std::basic_string<char, std::char_traits<char>, some_alloc3<char>> C;
90+
static_assert(!std::is_nothrow_move_assignable<C>::value, "");
91+
}
92+
#endif
93+
4694
#endif
4795
}

0 commit comments

Comments
 (0)