+
+ {
+ auto a = populate(amap);
+ VERIFY(a.size() == 9);
+ Z z{"de"};
+ auto it = a.equal_range(z);
+ VERIFY(it.first != a.end());
+ VERIFY(it.second != a.end());
+ VERIFY(it.first->first == std::string("dec"));
+ VERIFY(it.second->first == std::string("ghi"));
+
+ VERIFY(z.compares == 7);
+ }
+ {
+ auto a = populate(amap);
+ VERIFY(a.size() == 9);
+ Z z{"de"};
+ auto const& ca = a;
+ auto it = ca.equal_range(z);
+ VERIFY(it.first != ca.end());
+ VERIFY(it.second != ca.end());
+ VERIFY(it.first->first == std::string("dec"));
+ VERIFY(it.second->first == std::string("ghi"));
+
+ VERIFY(z.compares == 7);
+ }
+}
+
+int main()
+{
+ test_equal_range();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/multimap/operations/hetero/equal_range.cc
b/libstdc++-v3/testsuite/23_containers/multimap/operations/hetero/equal_range.cc
new file mode 100644
index 00000000000..e8558f480e9
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/multimap/operations/hetero/equal_range.cc
@@ -0,0 +1,114 @@
+// { dg-do run { target c++14 } }
+
+#include <map>
+#include <string>
+#include <utility>
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct Y;
+struct Z;
+
+struct X {
+ std::string s;
+ X(std::string str) : s(str) {}
+ X(Y&&);
+ X(const Y&);
+ X(Z&&);
+ X(const Z&);
+ friend bool operator<(X const& a, X const& b) { return a.s < b.s; }
+ friend bool operator==(X const& a, X const& b) { return a.s == b.s; }
+};
+
+struct Y {
+ std::string s;
+ Y() = default;
+ 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;
+ Y(std::string sv) : s(sv) {}
+ Y(int n) : s(std::string(n, 'a')) {}
+ Y(const Y& a, const Y& b) : s(a.s + "1" + b.s) { }
+ Y(const Y& a, Y&& b) : s(a.s + "2" + b.s) { b.s.clear(); }
+ Y(Y&& a, const Y& b) : s(a.s + "3" + b.s) { a.s.clear(); }
+ Y(Y&& a, Y&& b) : s(a.s + "4" + b.s) { a.s.clear(), b.s.clear(); }
+ friend bool operator<(Y const& a, Y const& b) { return a.s < b.s; }
+ friend bool operator<(X const& a, Y const& b) { return a.s < b.s; }
+ friend bool operator<(Y const& a, X 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>;
+
+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 sv) : s(sv) {}
+ Z(int n) : s(std::string(n, 'a')) {}
+ friend bool operator<(Z const& a, Z const& b) { return a.s < b.s; }
+ friend bool operator<(X const& a, Z const& b)
+ { return ++b.compares, a.s.substr(0, b.s.size()) < b.s; }
+ friend bool operator<(Z const& a, X const& b)
+ { return ++a.compares, a.s < b.s.substr(0, a.s.size()); }
+};
+
+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.
+
+template <typename T>
+T populate(T a)
+{
+ const std::string vs[] = { "dec", "ded", "dee", "def", "deg", "deh", "dei" };
+ for (auto const& v : vs)
+ a.insert(std::pair<const Y, Y>{Y{v}, Y{v}});
+ return a;
+}
+
+void test_equal_range()
+{
+ std::multimap<X, Y, cmp> amap{cmp{}};
+ amap.insert({{Y{"abc"}, 1}, {Y{"def"}, 2}, {Y{"ghi"}, 3}});
+
+ {
+ auto a = populate(amap);
+ VERIFY(a.size() == 10);
+ Z z{"de"};
+ auto it = a.equal_range(z);
+ VERIFY(it.first != a.end());
+ VERIFY(it.second != a.end());
+ VERIFY(it.first->first == std::string("dec"));
+ VERIFY(it.second->first == std::string("ghi"));
+
+ VERIFY(z.compares == 7);
+ }
+ {
+ auto a = populate(amap);
+ VERIFY(a.size() == 10);
+ Z z{"de"};
+ auto const& ca = a;
+ auto it = ca.equal_range(z);
+ VERIFY(it.first != ca.end());
+ VERIFY(it.second != ca.end());
+ VERIFY(it.first->first == std::string("dec"));
+ VERIFY(it.second->first == std::string("ghi"));
+
+ VERIFY(z.compares == 7);
+ }
+}
+
+int main()
+{
+ test_equal_range();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/multiset/operations/hetero/equal_range.cc
b/libstdc++-v3/testsuite/23_containers/multiset/operations/hetero/equal_range.cc
new file mode 100644
index 00000000000..052c218c097
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/multiset/operations/hetero/equal_range.cc
@@ -0,0 +1,114 @@
+// { dg-do run { target c++14 } }
+
+#include <set>
+#include <string>
+#include <utility>
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct Y;
+struct Z;
+
+struct X {
+ std::string s;
+ X(std::string str) : s(str) {}
+ X(Y&&);
+ X(const Y&);
+ X(Z&&);
+ X(const Z&);
+ friend bool operator<(X const& a, X const& b) { return a.s < b.s; }
+ friend bool operator==(X const& a, X const& b) { return a.s == b.s; }
+};
+
+struct Y {
+ std::string s;
+ Y() = default;
+ 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;
+ Y(std::string sv) : s(sv) {}
+ Y(int n) : s(std::string(n, 'a')) {}
+ Y(const Y& a, const Y& b) : s(a.s + "1" + b.s) { }
+ Y(const Y& a, Y&& b) : s(a.s + "2" + b.s) { b.s.clear(); }
+ Y(Y&& a, const Y& b) : s(a.s + "3" + b.s) { a.s.clear(); }
+ Y(Y&& a, Y&& b) : s(a.s + "4" + b.s) { a.s.clear(), b.s.clear(); }
+ friend bool operator<(Y const& a, Y const& b) { return a.s < b.s; }
+ friend bool operator<(X const& a, Y const& b) { return a.s < b.s; }
+ friend bool operator<(Y const& a, X 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>;
+
+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 sv) : s(sv) {}
+ Z(int n) : s(std::string(n, 'a')) {}
+ friend bool operator<(Z const& a, Z const& b) { return a.s < b.s; }
+ friend bool operator<(X const& a, Z const& b)
+ { return ++b.compares, a.s.substr(0, b.s.size()) < b.s; }
+ friend bool operator<(Z const& a, X const& b)
+ { return ++a.compares, a.s < b.s.substr(0, a.s.size()); }
+};
+
+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.
+
+template <typename T>
+T populate(T 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_equal_range()
+{
+ std::multiset<X, cmp> aset{cmp{}};
+ aset.insert({Y{"abc"}, Y{"def"}, Y{"ghi"}});
+
+ {
+ auto a = populate(aset);
+ VERIFY(a.size() == 10);
+ Z z{"de"};
+ auto it = a.equal_range(z);
+ VERIFY(it.first != a.end());
+ VERIFY(it.second != a.end());
+ VERIFY(*it.first == std::string("dec"));
+ VERIFY(*it.second == std::string("ghi"));
+
+ VERIFY(z.compares == 7);
+ }
+ {
+ auto a = populate(aset);
+ VERIFY(a.size() == 10);
+ Z z{"de"};
+ auto const& ca = a;
+ auto it = ca.equal_range(z);
+ VERIFY(it.first != ca.end());
+ VERIFY(it.second != ca.end());
+ VERIFY(*it.first == std::string("dec"));
+ VERIFY(*it.second == std::string("ghi"));
+
+ VERIFY(z.compares == 7);
+ }
+}
+
+int main()
+{
+ test_equal_range();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/set/operations/hetero/equal_range.cc
b/libstdc++-v3/testsuite/23_containers/set/operations/hetero/equal_range.cc
new file mode 100644
index 00000000000..1ba59140fdc
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/operations/hetero/equal_range.cc
@@ -0,0 +1,114 @@
+// { dg-do run { target c++14 } }
+
+#include <set>
+#include <string>
+#include <utility>
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct Y;
+struct Z;
+
+struct X {
+ std::string s;
+ X(std::string str) : s(str) {}
+ X(Y&&);
+ X(const Y&);
+ X(Z&&);
+ X(const Z&);
+ friend bool operator<(X const& a, X const& b) { return a.s < b.s; }
+ friend bool operator==(X const& a, X const& b) { return a.s == b.s; }
+};
+
+struct Y {
+ std::string s;
+ Y() = default;
+ 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;
+ Y(std::string sv) : s(sv) {}
+ Y(int n) : s(std::string(n, 'a')) {}
+ Y(const Y& a, const Y& b) : s(a.s + "1" + b.s) { }
+ Y(const Y& a, Y&& b) : s(a.s + "2" + b.s) { b.s.clear(); }
+ Y(Y&& a, const Y& b) : s(a.s + "3" + b.s) { a.s.clear(); }
+ Y(Y&& a, Y&& b) : s(a.s + "4" + b.s) { a.s.clear(), b.s.clear(); }
+ friend bool operator<(Y const& a, Y const& b) { return a.s < b.s; }
+ friend bool operator<(X const& a, Y const& b) { return a.s < b.s; }
+ friend bool operator<(Y const& a, X 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>;
+
+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 sv) : s(sv) {}
+ Z(int n) : s(std::string(n, 'a')) {}
+ friend bool operator<(Z const& a, Z const& b) { return a.s < b.s; }
+ friend bool operator<(X const& a, Z const& b)
+ { return ++b.compares, a.s.substr(0, b.s.size()) < b.s; }
+ friend bool operator<(Z const& a, X const& b)
+ { return ++a.compares, a.s < b.s.substr(0, a.s.size()); }
+};
+
+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.
+
+template <typename T>
+T populate(T 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_equal_range()
+{
+ std::set<X, cmp> aset{cmp{}};
+ aset.insert({Y{"abc"}, Y{"def"}, Y{"ghi"}});