Skip to content

Commit a8fea5b

Browse files
Implement LWG-4135 The helper lambda of std::erase for list should specify return type as bool (#5131)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 0053a14 commit a8fea5b

File tree

6 files changed

+181
-119
lines changed

6 files changed

+181
-119
lines changed

stl/inc/forward_list

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,7 @@ _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward
16161616
#if _HAS_CXX20
16171617
_EXPORT_STD template <class _Ty, class _Alloc, class _Uty>
16181618
forward_list<_Ty, _Alloc>::size_type erase(forward_list<_Ty, _Alloc>& _Cont, const _Uty& _Val) {
1619-
return _Cont.remove_if([&](_Ty& _Elem) -> bool { return _Elem == _Val; });
1619+
return _Cont.remove_if([&](const _Ty& _Elem) -> bool { return _Elem == _Val; });
16201620
}
16211621

16221622
_EXPORT_STD template <class _Ty, class _Alloc, class _Pr>

stl/inc/list

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1921,7 +1921,7 @@ _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Allo
19211921
#if _HAS_CXX20
19221922
_EXPORT_STD template <class _Ty, class _Alloc, class _Uty>
19231923
list<_Ty, _Alloc>::size_type erase(list<_Ty, _Alloc>& _Cont, const _Uty& _Val) {
1924-
return _Cont.remove_if([&](_Ty& _Elem) -> bool { return _Elem == _Val; });
1924+
return _Cont.remove_if([&](const _Ty& _Elem) -> bool { return _Elem == _Val; });
19251925
}
19261926

19271927
_EXPORT_STD template <class _Ty, class _Alloc, class _Pr>

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ tests\P1206R7_vector_assign_range
588588
tests\P1206R7_vector_from_range
589589
tests\P1206R7_vector_insert_range
590590
tests\P1208R6_source_location
591+
tests\P1209R0_erase_if_erase
591592
tests\P1223R5_ranges_alg_find_last
592593
tests\P1223R5_ranges_alg_find_last_if
593594
tests\P1223R5_ranges_alg_find_last_if_not

tests/std/tests/Dev11_0000000_user_defined_literals/test.cpp

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -435,123 +435,6 @@ int main() {
435435
assert(!const_us.contains(1));
436436
assert(!const_ums.contains(3));
437437
}
438-
439-
// P1209R0 erase_if(), erase()
440-
{
441-
// Note that the standard actually requires these to be copyable. As an extension, we want
442-
// to ensure we don't copy them, because copying some functors (e.g. std::function) is comparatively
443-
// expensive, and even for relatively cheap to copy function objects we care (somewhat) about debug
444-
// mode perf.
445-
struct no_copy {
446-
no_copy() = default;
447-
no_copy(const no_copy&) = delete;
448-
no_copy(no_copy&&) = default;
449-
no_copy& operator=(const no_copy&) = delete;
450-
no_copy& operator=(no_copy&&) = delete;
451-
};
452-
453-
struct is_vowel : no_copy {
454-
bool operator()(const char c) const {
455-
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
456-
}
457-
};
458-
459-
std::string str1{"cute fluffy kittens"};
460-
const auto str1_removed = std::erase_if(str1, is_vowel{});
461-
assert(str1 == "ct flffy kttns");
462-
assert(str1_removed == 5);
463-
464-
std::string str2{"asynchronous beat"};
465-
const auto str2_removed = std::erase(str2, 'a');
466-
assert(str2 == "synchronous bet");
467-
assert(str2_removed == 2);
468-
469-
struct is_odd : no_copy {
470-
bool operator()(const int i) const {
471-
return i % 2 != 0;
472-
}
473-
};
474-
475-
std::deque<int> d{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
476-
const auto d_removed = std::erase_if(d, is_odd{});
477-
assert((d == std::deque<int>{2, 4, 6, 6, 4, 2}));
478-
assert(d_removed == 7);
479-
const auto d_removed2 = std::erase(d, 4);
480-
assert((d == std::deque<int>{2, 6, 6, 2}));
481-
assert(d_removed2 == 2);
482-
483-
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
484-
const auto v_removed = std::erase_if(v, is_odd{});
485-
assert((v == std::vector<int>{2, 4, 6, 6, 4, 2}));
486-
assert(v_removed == 7);
487-
const auto v_removed2 = std::erase(v, 4);
488-
assert((v == std::vector<int>{2, 6, 6, 2}));
489-
assert(v_removed2 == 2);
490-
491-
std::forward_list<int> fl{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
492-
const auto fl_removed = std::erase_if(fl, is_odd{});
493-
assert((fl == std::forward_list<int>{2, 4, 6, 6, 4, 2}));
494-
assert(fl_removed == 7);
495-
const auto fl_removed2 = std::erase(fl, 4);
496-
assert((fl == std::forward_list<int>{2, 6, 6, 2}));
497-
assert(fl_removed2 == 2);
498-
499-
std::list<int> l{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
500-
const auto l_removed = std::erase_if(l, is_odd{});
501-
assert((l == std::list<int>{2, 4, 6, 6, 4, 2}));
502-
assert(l_removed == 7);
503-
const auto l_removed2 = std::erase(l, 4);
504-
assert((l == std::list<int>{2, 6, 6, 2}));
505-
assert(l_removed2 == 2);
506-
507-
struct is_first_odd : no_copy {
508-
bool operator()(const std::pair<const int, int>& p) const {
509-
return p.first % 2 != 0;
510-
}
511-
};
512-
513-
std::map<int, int> m{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
514-
const auto m_removed = std::erase_if(m, is_first_odd{});
515-
assert((m == std::map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
516-
assert(m_removed == 4);
517-
518-
std::multimap<int, int> mm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
519-
const auto mm_removed = std::erase_if(mm, is_first_odd{});
520-
assert((mm == std::multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
521-
assert(mm_removed == 4);
522-
523-
std::set<int> s{1, 2, 3, 4, 5, 6, 7};
524-
const auto s_removed = std::erase_if(s, is_odd{});
525-
assert((s == std::set<int>{2, 4, 6}));
526-
assert(s_removed == 4);
527-
528-
std::multiset<int> ms{1, 2, 3, 4, 5, 6, 7};
529-
const auto ms_removed = std::erase_if(ms, is_odd{});
530-
assert((ms == std::multiset<int>{2, 4, 6}));
531-
assert(ms_removed == 4);
532-
533-
// Note that unordered equality considers permutations.
534-
535-
std::unordered_map<int, int> um{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
536-
const auto um_removed = std::erase_if(um, is_first_odd{});
537-
assert((um == std::unordered_map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
538-
assert(um_removed == 4);
539-
540-
std::unordered_multimap<int, int> umm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
541-
const auto umm_removed = std::erase_if(umm, is_first_odd{});
542-
assert((umm == std::unordered_multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
543-
assert(umm_removed == 4);
544-
545-
std::unordered_set<int> us{1, 2, 3, 4, 5, 6, 7};
546-
const auto us_removed = std::erase_if(us, is_odd{});
547-
assert((us == std::unordered_set<int>{2, 4, 6}));
548-
assert(us_removed == 4);
549-
550-
std::unordered_multiset<int> ums{1, 2, 3, 4, 5, 6, 7};
551-
const auto ums_removed = std::erase_if(ums, is_odd{});
552-
assert((ums == std::unordered_multiset<int>{2, 4, 6}));
553-
assert(ums_removed == 4);
554-
}
555438
#endif // _HAS_CXX20
556439

557440
// P0007R1 as_const()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\usual_20_matrix.lst
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <cassert>
5+
#include <deque>
6+
#include <forward_list>
7+
#include <list>
8+
#include <map>
9+
#include <set>
10+
#include <string>
11+
#include <unordered_map>
12+
#include <unordered_set>
13+
#include <utility>
14+
#include <vector>
15+
16+
// Note that the standard actually requires these to be copyable. As an extension, we want to ensure we don't copy them,
17+
// because copying some functors (e.g. std::function) is comparatively expensive, and even for relatively cheap to copy
18+
// function objects we care (somewhat) about debug mode perf.
19+
struct no_copy {
20+
no_copy() = default;
21+
no_copy(const no_copy&) = delete;
22+
no_copy(no_copy&&) = default;
23+
no_copy& operator=(const no_copy&) = delete;
24+
no_copy& operator=(no_copy&&) = delete;
25+
};
26+
27+
struct is_vowel : no_copy {
28+
constexpr bool operator()(const char c) const {
29+
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
30+
}
31+
};
32+
33+
struct is_odd : no_copy {
34+
constexpr bool operator()(const int i) const {
35+
return i % 2 != 0;
36+
}
37+
};
38+
39+
struct is_first_odd : no_copy {
40+
bool operator()(const std::pair<const int, int>& p) const {
41+
return p.first % 2 != 0;
42+
}
43+
};
44+
45+
constexpr bool test_string() {
46+
std::string str1{"cute fluffy kittens"};
47+
const auto str1_removed = std::erase_if(str1, is_vowel{});
48+
assert(str1 == "ct flffy kttns");
49+
assert(str1_removed == 5);
50+
51+
std::string str2{"asynchronous beat"};
52+
const auto str2_removed = std::erase(str2, 'a');
53+
assert(str2 == "synchronous bet");
54+
assert(str2_removed == 2);
55+
56+
return true;
57+
}
58+
59+
template <class SequenceContainer>
60+
constexpr bool test_sequence_container() {
61+
SequenceContainer c{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5,
62+
0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0};
63+
64+
{
65+
const auto removed1 = std::erase_if(c, is_odd{});
66+
assert(removed1 == 31);
67+
const SequenceContainer expected1{4, 2, 6, 8, 2, 8, 4, 6, 2, 6, 4, 8, 2, 0, 2, 8, 8, 4, 6, 0};
68+
assert(c == expected1);
69+
}
70+
71+
{
72+
const auto removed2 = std::erase(c, 8);
73+
assert(removed2 == 5);
74+
const SequenceContainer expected2{4, 2, 6, 2, 4, 6, 2, 6, 4, 2, 0, 2, 4, 6, 0};
75+
assert(c == expected2);
76+
}
77+
78+
return true;
79+
}
80+
81+
// Also test LWG-4135 "The helper lambda of std::erase for list should specify return type as bool"
82+
83+
template <bool B>
84+
struct pinned_condition {
85+
explicit pinned_condition() = default;
86+
pinned_condition(const pinned_condition&) = delete;
87+
pinned_condition& operator=(const pinned_condition&) = delete;
88+
89+
operator bool() const {
90+
return B;
91+
}
92+
93+
pinned_condition<!B> operator!() const {
94+
return {};
95+
}
96+
};
97+
98+
struct lwg_4135_src {
99+
static constexpr pinned_condition<true> result{};
100+
101+
friend void operator==(int&, const lwg_4135_src&) = delete;
102+
friend void operator==(const lwg_4135_src&, int&) = delete;
103+
104+
friend const pinned_condition<true>& operator==(const lwg_4135_src&, const int&) {
105+
return result;
106+
}
107+
friend const pinned_condition<true>& operator==(const int&, const lwg_4135_src&) {
108+
return result;
109+
}
110+
};
111+
112+
template <class ListContainer>
113+
void test_list_erase() {
114+
ListContainer ls2{42, 1729};
115+
const auto ls2_removed = std::erase(ls2, lwg_4135_src{});
116+
assert(ls2.empty());
117+
assert(ls2_removed == 2);
118+
}
119+
120+
static_assert(test_string());
121+
static_assert(test_sequence_container<std::vector<int>>());
122+
123+
int main() {
124+
test_string();
125+
test_sequence_container<std::deque<int>>();
126+
test_sequence_container<std::forward_list<int>>();
127+
test_sequence_container<std::list<int>>();
128+
test_sequence_container<std::vector<int>>();
129+
130+
test_list_erase<std::forward_list<int>>();
131+
test_list_erase<std::list<int>>();
132+
133+
std::map<int, int> m{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
134+
const auto m_removed = std::erase_if(m, is_first_odd{});
135+
assert((m == std::map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
136+
assert(m_removed == 4);
137+
138+
std::multimap<int, int> mm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
139+
const auto mm_removed = std::erase_if(mm, is_first_odd{});
140+
assert((mm == std::multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
141+
assert(mm_removed == 4);
142+
143+
std::set<int> s{1, 2, 3, 4, 5, 6, 7};
144+
const auto s_removed = std::erase_if(s, is_odd{});
145+
assert((s == std::set<int>{2, 4, 6}));
146+
assert(s_removed == 4);
147+
148+
std::multiset<int> ms{1, 2, 3, 4, 5, 6, 7};
149+
const auto ms_removed = std::erase_if(ms, is_odd{});
150+
assert((ms == std::multiset<int>{2, 4, 6}));
151+
assert(ms_removed == 4);
152+
153+
// Note that unordered equality considers permutations.
154+
155+
std::unordered_map<int, int> um{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
156+
const auto um_removed = std::erase_if(um, is_first_odd{});
157+
assert((um == std::unordered_map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
158+
assert(um_removed == 4);
159+
160+
std::unordered_multimap<int, int> umm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
161+
const auto umm_removed = std::erase_if(umm, is_first_odd{});
162+
assert((umm == std::unordered_multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
163+
assert(umm_removed == 4);
164+
165+
std::unordered_set<int> us{1, 2, 3, 4, 5, 6, 7};
166+
const auto us_removed = std::erase_if(us, is_odd{});
167+
assert((us == std::unordered_set<int>{2, 4, 6}));
168+
assert(us_removed == 4);
169+
170+
std::unordered_multiset<int> ums{1, 2, 3, 4, 5, 6, 7};
171+
const auto ums_removed = std::erase_if(ums, is_odd{});
172+
assert((ums == std::unordered_multiset<int>{2, 4, 6}));
173+
assert(ums_removed == 4);
174+
}

0 commit comments

Comments
 (0)