| // { dg-do run { target c++26 } } |
| |
| #include <set> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <compare> |
| #include <cstring> |
| #include <testsuite_hooks.h> |
| |
| struct Y; |
| struct Z; |
| |
| struct X { |
| std::string s; |
| X(std::string_view str) : s(str) {} |
| X(Y&& y); |
| X(const Y& y); |
| X(Z&& z); |
| X(const Z& z); |
| |
| friend auto operator<=>(X const& a, X const& b) = default; |
| }; |
| |
| struct Y { |
| std::string s; |
| Y() = default; |
| Y(std::string_view sv) : s(sv) {} |
| Y(int a, int b = 0) : s(std::string('a', a + b)) {} |
| Y(Y&& y) : s(std::move(y.s)) { y.s.clear(); } |
| Y(const Y& y) = default; |
| Y& operator=(Y&& y) { s = std::move(y.s); y.s.clear(); return *this; } |
| Y& operator=(const Y& y) = default; |
| friend auto operator<=>(Y const& a, Y const& b) = default; |
| friend auto operator<=>(X const& a, Y const& b) { return a.s <=> b.s; } |
| }; |
| |
| X::X(Y&& y) : s(std::move(y.s)) { y.s.clear(); } |
| X::X(const Y& y) : s(y.s) {} |
| |
| using cmp = std::less<void>; |
| |
| void test_root() |
| { |
| std::set<X, cmp> aset{cmp{}}; |
| aset.insert(Y{"def"}); |
| VERIFY(aset.size() == 1); |
| VERIFY(aset.contains(X{"def"})); |
| aset.insert(Y{"abc"}); |
| VERIFY(aset.size() == 2); |
| VERIFY(aset.contains(X{"abc"})); |
| aset.insert(Y{"ghi"}); |
| VERIFY(aset.size() == 3); |
| VERIFY(aset.contains(X{"ghi"})); |
| } |
| |
| void test_insert() |
| { |
| std::set<X, cmp> aset{cmp{}}; |
| aset.insert({Y{"abc"}, Y{"def"}, Y{"ghi"}}); |
| |
| { // Fail |
| auto a = aset; |
| Y y{"def"}; |
| auto [it, res] = a.insert(y); |
| VERIFY(!res); |
| VERIFY(a.size() == 3); |
| VERIFY(it->s == "def"); |
| VERIFY(y.s.size() == 3); // not moved |
| } |
| { // Fail, move |
| auto a = aset; |
| Y y{"def"}; |
| auto [it, res] = a.insert(std::move(y)); |
| VERIFY(!res); |
| VERIFY(a.size() == 3); |
| VERIFY(it->s == "def"); |
| VERIFY(y.s.size() == 3); // not moved |
| } |
| { // Succeed |
| auto a = aset; |
| Y y{"deg"}; |
| auto [it, res] = a.insert(y); |
| VERIFY(res); |
| VERIFY(a.size() == 4); |
| VERIFY(it->s == "deg"); |
| VERIFY(y.s.size() == 3); // not moved |
| } |
| { // Succeed, move |
| auto a = aset; |
| Y y{"deg"}; |
| auto [it, res] = a.insert(std::move(y)); |
| VERIFY(res); |
| VERIFY(a.size() == 4); |
| VERIFY(it->s == "deg"); |
| VERIFY(y.s.empty()); // moved |
| } |
| |
| |
| { // Hinted, fail |
| auto a = aset; |
| Y y{"def"}; |
| auto it = a.insert(a.begin(), y); |
| VERIFY(a.size() == 3); |
| VERIFY(it->s == "def"); |
| VERIFY(y.s.size() == 3); // not moved |
| } |
| { // Hinted, fail, move |
| auto a = aset; |
| Y y{"def"}; |
| auto it = a.insert(a.begin(), std::move(y)); |
| VERIFY(a.size() == 3); |
| VERIFY(it->s == "def"); |
| VERIFY(y.s.size() == 3); // not moved |
| } |
| { // Hinted, succeed |
| auto a = aset; |
| Y y{"deh"}; |
| auto it = a.insert(a.begin(), y); |
| VERIFY(a.size() == 4); |
| VERIFY(it->s == "deh"); |
| VERIFY(y.s.size() == 3); // not moved |
| } |
| { // Hinted, succeed, move |
| auto a = aset; |
| Y y{"deh"}; |
| auto it = a.insert(a.begin(), std::move(y)); |
| VERIFY(a.size() == 4); |
| VERIFY(it->s == "deh"); |
| VERIFY(y.s.empty()); // moved |
| } |
| } |
| |
| struct Z { |
| std::string s; |
| mutable int compares = 0; |
| |
| Z() = default; |
| Z(Z&& z) : s(std::move(z.s)) { z.s.clear(); } |
| Z(const Z& z) = default; |
| Z& operator=(Z&& z) { s = std::move(z.s); z.s.clear(); return *this; } |
| Z& operator=(const Z& z) = default; |
| Z(std::string_view sv) : s(sv) {} |
| Z(int n) : s(std::string(n, 'a')) {} |
| friend auto operator<=>(Z const& a, Z const& b) = default; |
| friend auto operator<=>(X const& a, Z const& b) |
| { return ++b.compares, a.s.substr(0, b.s.size()) <=> b.s; } |
| }; |
| |
| X::X(Z&& z) : s(std::move(z.s)) { z.s.clear(); } |
| X::X(const Z& z) : s(z.s) {} |
| |
| // A heterogeneous key type like Z here that compares equal |
| // if it matches just the first part of the key is allowed, |
| // which affects op[], try_emplace, and insert_or_assign. |
| |
| auto populate(auto a) |
| { |
| const std::string vs[] = { "dec", "ded", "dee", "def", "deg", "deh", "dei" }; |
| for (auto const& v : vs) |
| a.insert(Y{v}); |
| return a; |
| } |
| |
| void test_insert_prefix() |
| { |
| std::set<X, cmp> aset{cmp{}}; |
| aset.insert({Y{"abc"}, Y{"def"}, Y{"ghi"}}); |
| { |
| { // Already there, fail. |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto [it, res] = a.insert(std::move(z)); |
| VERIFY(!res); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(!a.contains(Y{"de"})); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); // not moved from |
| VERIFY(z.compares > 3); |
| } |
| } |
| { |
| { // hinted, succeed |
| Z z{"aaa"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.begin(), std::move(z)); |
| VERIFY(a.size() == 10); |
| VERIFY(*it == X{"aaa"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.empty()); // moved from |
| VERIFY(z.compares == 1); |
| } |
| } |
| { // Hinted, already there, fail. |
| { |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.begin(), std::move(z)); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); |
| VERIFY(z.compares > 3); |
| } |
| { |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"dec"}), std::move(z)); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); |
| VERIFY(z.compares == 3); |
| } |
| { |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"def"}), std::move(z)); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); |
| VERIFY(z.compares > 3); |
| } |
| { |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"dei"}), std::move(z)); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); |
| VERIFY(z.compares > 3); |
| } |
| { |
| Z z{"df"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"dei"}), std::move(z)); |
| VERIFY(a.size() == 10); |
| VERIFY(*it == X{"df"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.empty()); // moved from |
| VERIFY(z.compares == 3); |
| } |
| { |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"ghi"}), std::move(z)); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); |
| VERIFY(z.compares > 3); |
| } |
| { |
| Z z{"df"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"ghi"}), std::move(z)); |
| VERIFY(a.size() == 10); |
| VERIFY(*it == X{"df"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.empty()); // moved from |
| VERIFY(z.compares == 2); |
| } |
| { |
| Z z{"de"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.end(), std::move(z)); |
| VERIFY(a.size() == 9); |
| VERIFY(*it == X{"dec"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.size() == 2); |
| VERIFY(z.compares > 3); |
| } |
| { |
| Z z{"jkl"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.find(X{"ghi"}), std::move(z)); |
| VERIFY(a.size() == 10); |
| VERIFY(*it == X{"jkl"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.empty()); // moved from |
| VERIFY(z.compares == 2); |
| } |
| { |
| Z z{"jkl"}; |
| auto a = populate(aset); |
| auto it = a.insert(a.end(), std::move(z)); |
| VERIFY(a.size() == 10); |
| VERIFY(*it == X{"jkl"}); |
| VERIFY(a.contains(Y{"dec"})); |
| VERIFY(a.contains(Y{"ded"})); |
| VERIFY(a.contains(Y{"dee"})); |
| VERIFY(a.contains(Y{"def"})); |
| VERIFY(a.contains(Y{"deg"})); |
| VERIFY(a.contains(Y{"deh"})); |
| VERIFY(a.contains(Y{"dei"})); |
| VERIFY(z.s.empty()); // moved from |
| VERIFY(z.compares == 1); |
| } |
| } |
| } |
| |
| int main() |
| { |
| test_root(); |
| test_insert(); |
| test_insert_prefix(); |
| } |