Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement LWG-4135 The helper lambda of std::erase for list should specify return type as bool #5131

Merged
merged 5 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stl/inc/forward_list
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,7 @@ _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward
#if _HAS_CXX20
_EXPORT_STD template <class _Ty, class _Alloc, class _Uty>
forward_list<_Ty, _Alloc>::size_type erase(forward_list<_Ty, _Alloc>& _Cont, const _Uty& _Val) {
return _Cont.remove_if([&](_Ty& _Elem) -> bool { return _Elem == _Val; });
return _Cont.remove_if([&](const _Ty& _Elem) -> bool { return _Elem == _Val; });
}

_EXPORT_STD template <class _Ty, class _Alloc, class _Pr>
Expand Down
2 changes: 1 addition & 1 deletion stl/inc/list
Original file line number Diff line number Diff line change
Expand Up @@ -1921,7 +1921,7 @@ _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Allo
#if _HAS_CXX20
_EXPORT_STD template <class _Ty, class _Alloc, class _Uty>
list<_Ty, _Alloc>::size_type erase(list<_Ty, _Alloc>& _Cont, const _Uty& _Val) {
return _Cont.remove_if([&](_Ty& _Elem) -> bool { return _Elem == _Val; });
return _Cont.remove_if([&](const _Ty& _Elem) -> bool { return _Elem == _Val; });
}

_EXPORT_STD template <class _Ty, class _Alloc, class _Pr>
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ tests\P1206R7_vector_assign_range
tests\P1206R7_vector_from_range
tests\P1206R7_vector_insert_range
tests\P1208R6_source_location
tests\P1209R0_erase_if_erase
tests\P1223R5_ranges_alg_find_last
tests\P1223R5_ranges_alg_find_last_if
tests\P1223R5_ranges_alg_find_last_if_not
Expand Down
117 changes: 0 additions & 117 deletions tests/std/tests/Dev11_0000000_user_defined_literals/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,123 +435,6 @@ int main() {
assert(!const_us.contains(1));
assert(!const_ums.contains(3));
}

// P1209R0 erase_if(), erase()
{
// Note that the standard actually requires these to be copyable. As an extension, we want
// to ensure we don't copy them, because copying some functors (e.g. std::function) is comparatively
// expensive, and even for relatively cheap to copy function objects we care (somewhat) about debug
// mode perf.
struct no_copy {
no_copy() = default;
no_copy(const no_copy&) = delete;
no_copy(no_copy&&) = default;
no_copy& operator=(const no_copy&) = delete;
no_copy& operator=(no_copy&&) = delete;
};

struct is_vowel : no_copy {
bool operator()(const char c) const {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
};

std::string str1{"cute fluffy kittens"};
const auto str1_removed = std::erase_if(str1, is_vowel{});
assert(str1 == "ct flffy kttns");
assert(str1_removed == 5);

std::string str2{"asynchronous beat"};
const auto str2_removed = std::erase(str2, 'a');
assert(str2 == "synchronous bet");
assert(str2_removed == 2);

struct is_odd : no_copy {
bool operator()(const int i) const {
return i % 2 != 0;
}
};

std::deque<int> d{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
const auto d_removed = std::erase_if(d, is_odd{});
assert((d == std::deque<int>{2, 4, 6, 6, 4, 2}));
assert(d_removed == 7);
const auto d_removed2 = std::erase(d, 4);
assert((d == std::deque<int>{2, 6, 6, 2}));
assert(d_removed2 == 2);

std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
const auto v_removed = std::erase_if(v, is_odd{});
assert((v == std::vector<int>{2, 4, 6, 6, 4, 2}));
assert(v_removed == 7);
const auto v_removed2 = std::erase(v, 4);
assert((v == std::vector<int>{2, 6, 6, 2}));
assert(v_removed2 == 2);

std::forward_list<int> fl{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
const auto fl_removed = std::erase_if(fl, is_odd{});
assert((fl == std::forward_list<int>{2, 4, 6, 6, 4, 2}));
assert(fl_removed == 7);
const auto fl_removed2 = std::erase(fl, 4);
assert((fl == std::forward_list<int>{2, 6, 6, 2}));
assert(fl_removed2 == 2);

std::list<int> l{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
const auto l_removed = std::erase_if(l, is_odd{});
assert((l == std::list<int>{2, 4, 6, 6, 4, 2}));
assert(l_removed == 7);
const auto l_removed2 = std::erase(l, 4);
assert((l == std::list<int>{2, 6, 6, 2}));
assert(l_removed2 == 2);

struct is_first_odd : no_copy {
bool operator()(const std::pair<const int, int>& p) const {
return p.first % 2 != 0;
}
};

std::map<int, int> m{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto m_removed = std::erase_if(m, is_first_odd{});
assert((m == std::map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(m_removed == 4);

std::multimap<int, int> mm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto mm_removed = std::erase_if(mm, is_first_odd{});
assert((mm == std::multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(mm_removed == 4);

std::set<int> s{1, 2, 3, 4, 5, 6, 7};
const auto s_removed = std::erase_if(s, is_odd{});
assert((s == std::set<int>{2, 4, 6}));
assert(s_removed == 4);

std::multiset<int> ms{1, 2, 3, 4, 5, 6, 7};
const auto ms_removed = std::erase_if(ms, is_odd{});
assert((ms == std::multiset<int>{2, 4, 6}));
assert(ms_removed == 4);

// Note that unordered equality considers permutations.

std::unordered_map<int, int> um{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto um_removed = std::erase_if(um, is_first_odd{});
assert((um == std::unordered_map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(um_removed == 4);

std::unordered_multimap<int, int> umm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto umm_removed = std::erase_if(umm, is_first_odd{});
assert((umm == std::unordered_multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(umm_removed == 4);

std::unordered_set<int> us{1, 2, 3, 4, 5, 6, 7};
const auto us_removed = std::erase_if(us, is_odd{});
assert((us == std::unordered_set<int>{2, 4, 6}));
assert(us_removed == 4);

std::unordered_multiset<int> ums{1, 2, 3, 4, 5, 6, 7};
const auto ums_removed = std::erase_if(ums, is_odd{});
assert((ums == std::unordered_multiset<int>{2, 4, 6}));
assert(ums_removed == 4);
}
#endif // _HAS_CXX20

// P0007R1 as_const()
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P1209R0_erase_if_erase/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_20_matrix.lst
174 changes: 174 additions & 0 deletions tests/std/tests/P1209R0_erase_if_erase/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

// Note that the standard actually requires these to be copyable. As an extension, we want to ensure we don't copy them,
// because copying some functors (e.g. std::function) is comparatively expensive, and even for relatively cheap to copy
// function objects we care (somewhat) about debug mode perf.
struct no_copy {
no_copy() = default;
no_copy(const no_copy&) = delete;
no_copy(no_copy&&) = default;
no_copy& operator=(const no_copy&) = delete;
no_copy& operator=(no_copy&&) = delete;
};

struct is_vowel : no_copy {
constexpr bool operator()(const char c) const {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
};

struct is_odd : no_copy {
constexpr bool operator()(const int i) const {
return i % 2 != 0;
}
};

struct is_first_odd : no_copy {
bool operator()(const std::pair<const int, int>& p) const {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
return p.first % 2 != 0;
}
};

constexpr bool test_string() {
std::string str1{"cute fluffy kittens"};
const auto str1_removed = std::erase_if(str1, is_vowel{});
assert(str1 == "ct flffy kttns");
assert(str1_removed == 5);

std::string str2{"asynchronous beat"};
const auto str2_removed = std::erase(str2, 'a');
assert(str2 == "synchronous bet");
assert(str2_removed == 2);

return true;
}

template <class SequenceContainer>
constexpr bool test_sequence_container() {
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,
0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0};

{
const auto removed1 = std::erase_if(c, is_odd{});
assert(removed1 == 31);
const SequenceContainer expected1{4, 2, 6, 8, 2, 8, 4, 6, 2, 6, 4, 8, 2, 0, 2, 8, 8, 4, 6, 0};
assert(c == expected1);
}

{
const auto removed2 = std::erase(c, 8);
assert(removed2 == 5);
const SequenceContainer expected2{4, 2, 6, 2, 4, 6, 2, 6, 4, 2, 0, 2, 4, 6, 0};
assert(c == expected2);
}

return true;
}

// Also test LWG-4135 "The helper lambda of std::erase for list should specify return type as bool"

template <bool B>
struct pinned_condition {
explicit pinned_condition() = default;
pinned_condition(const pinned_condition&) = delete;
pinned_condition& operator=(const pinned_condition&) = delete;

operator bool() const {
return B;
}

pinned_condition<!B> operator!() const {
return {};
}
};

struct lwg_4135_src {
static constexpr pinned_condition<true> result{};

friend void operator==(int&, const lwg_4135_src&) = delete;
friend void operator==(const lwg_4135_src&, int&) = delete;

friend const pinned_condition<true>& operator==(const lwg_4135_src&, const int&) {
return result;
}
friend const pinned_condition<true>& operator==(const int&, const lwg_4135_src&) {
return result;
}
};

template <class ListContainer>
void test_list_erase() {
ListContainer ls2{42, 1729};
const auto ls2_removed = std::erase(ls2, lwg_4135_src{});
assert(ls2.empty());
assert(ls2_removed == 2);
}

static_assert(test_string());
static_assert(test_sequence_container<std::vector<int>>());

int main() {
test_string();
test_sequence_container<std::deque<int>>();
test_sequence_container<std::forward_list<int>>();
test_sequence_container<std::list<int>>();
test_sequence_container<std::vector<int>>();

test_list_erase<std::forward_list<int>>();
test_list_erase<std::list<int>>();

std::map<int, int> m{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto m_removed = std::erase_if(m, is_first_odd{});
assert((m == std::map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(m_removed == 4);

std::multimap<int, int> mm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto mm_removed = std::erase_if(mm, is_first_odd{});
assert((mm == std::multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(mm_removed == 4);

std::set<int> s{1, 2, 3, 4, 5, 6, 7};
const auto s_removed = std::erase_if(s, is_odd{});
assert((s == std::set<int>{2, 4, 6}));
assert(s_removed == 4);

std::multiset<int> ms{1, 2, 3, 4, 5, 6, 7};
const auto ms_removed = std::erase_if(ms, is_odd{});
assert((ms == std::multiset<int>{2, 4, 6}));
assert(ms_removed == 4);

// Note that unordered equality considers permutations.

std::unordered_map<int, int> um{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto um_removed = std::erase_if(um, is_first_odd{});
assert((um == std::unordered_map<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(um_removed == 4);

std::unordered_multimap<int, int> umm{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}, {6, 60}, {7, 70}};
const auto umm_removed = std::erase_if(umm, is_first_odd{});
assert((umm == std::unordered_multimap<int, int>{{2, 20}, {4, 40}, {6, 60}}));
assert(umm_removed == 4);

std::unordered_set<int> us{1, 2, 3, 4, 5, 6, 7};
const auto us_removed = std::erase_if(us, is_odd{});
assert((us == std::unordered_set<int>{2, 4, 6}));
assert(us_removed == 4);

std::unordered_multiset<int> ums{1, 2, 3, 4, 5, 6, 7};
const auto ums_removed = std::erase_if(ums, is_odd{});
assert((ums == std::unordered_multiset<int>{2, 4, 6}));
assert(ums_removed == 4);
}