Hi
libstdc++: Always instantiate key_type to compute hash code [PR115285]
Even if it is possible to compute a hash code from the inserted
arguments
we need to instantiate the key_type to guaranty hash code consistency.
Preserve the lazy instantiation of the mapped_type in the context of
associative containers.
libstdc++-v3/ChangeLog:
PR libstdc++/115285
* include/bits/hashtable.h (_S_forward_key<_Kt>): Always
return a temporary
key_type instance.
* testsuite/23_containers/unordered_map/96088.cc: Adapt to
additional instanciation.
Also check that mapped_type is not instantiated when there
is no insertion.
* testsuite/23_containers/unordered_multimap/96088.cc:
Adapt to additional
instanciation.
* testsuite/23_containers/unordered_multiset/96088.cc:
Likewise.
* testsuite/23_containers/unordered_set/96088.cc: Likewise.
* testsuite/23_containers/unordered_set/pr115285.cc: New
test case.
Tested under Linux x64,
ok to commit ?
François
diff --git a/libstdc++-v3/include/bits/hashtable.h
b/libstdc++-v3/include/bits/hashtable.h
index b4e8e4d3fb2..efc155bdfa4 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -930,10 +930,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_insert_unique(_Kt&&, _Arg&&, _NodeGenerator&);
template<typename _Kt>
- static __conditional_t<
- __and_<__is_nothrow_invocable<_Hash&, const key_type&>,
- __not_<__is_nothrow_invocable<_Hash&, _Kt>>>::value,
- key_type, _Kt&&>
+ key_type
_S_forward_key(_Kt&& __k)
{ return std::forward<_Kt>(__k); }
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
index 2065caab6c5..b5be7d06aa0 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc
@@ -29,46 +29,45 @@
#include <testsuite_hooks.h>
#include <replacement_memory_operators.h>
-static constexpr std::initializer_list<std::pair<const char*, int>> lst = {
- {"long_str_for_dynamic_allocating", 1}
-};
+static constexpr std::initializer_list<std::pair<const char*, const char*>>
lst =
+ { { "long_str_for_dynamic_allocation", "long_str_for_dynamic_allocation" } };
void
test01()
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int> um;
+ std::unordered_map<std::string, std::string> um;
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 4 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
void
test02()
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
std::hash<std::string_view>,
std::equal_to<std::string_view>> um;
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
std::size_t
@@ -84,20 +83,20 @@ test11()
typedef std::size_t (*hash_string_t)(const std::string&) noexcept;
__gnu_test::counter::reset();
hash_string_t hasher = &hash_string_f;
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
hash_string_t,
std::equal_to<std::string>> um(0, hasher);
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 4 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
std::size_t
@@ -113,19 +112,19 @@ test12()
typedef std::size_t (*hash_stringview_t) (const std::string_view&) noexcept;
__gnu_test::counter::reset();
hash_stringview_t hasher = &hash_string_view_f;
- std::unordered_map<std::string, int, hash_stringview_t,
+ std::unordered_map<std::string, std::string, hash_stringview_t,
std::equal_to<std::string_view>> um(0, hasher);
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
struct hash_string_functor
@@ -142,20 +141,20 @@ void
test21()
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
hash_string_functor,
std::equal_to<std::string>> um;
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 4 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
struct hash_string_view_noexcept_functor
@@ -172,20 +171,20 @@ void
test22()
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
hash_string_view_noexcept_functor,
std::equal_to<std::string_view>> um;
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
struct hash_string_view_functor
@@ -202,40 +201,41 @@ void
test23()
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
hash_string_view_functor,
std::equal_to<std::string_view>> um;
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
um.insert(lst.begin(), lst.end());
VERIFY( um.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 5 );
}
void
test03()
{
- std::vector<std::pair<std::string, int>> v;
+ std::vector<std::pair<std::string, std::string>> v;
v.insert(v.end(), lst.begin(), lst.end());
const auto origin = __gnu_test::counter::count();
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
std::hash<std::string_view>,
std::equal_to<std::string_view>> um;
um.insert(v.begin(), v.end());
VERIFY( um.size() == 1 );
- // Allocate array of buckets, a node, and the std::string (unless COW).
- constexpr std::size_t increments = _GLIBCXX_USE_CXX11_ABI ? 3 : 2;
+ // Allocate array of buckets, a node, the std::string value and the
+ // std::string key (unless COW).
+ constexpr std::size_t increments = _GLIBCXX_USE_CXX11_ABI ? 4 : 3;
VERIFY( __gnu_test::counter::count() == origin + increments );
VERIFY( __gnu_test::counter::get()._M_increments == increments );
@@ -250,7 +250,7 @@ test03()
{
__gnu_test::counter::reset();
- std::unordered_map<std::string, int,
+ std::unordered_map<std::string, std::string,
std::hash<std::string_view>,
std::equal_to<std::string_view>> um;
um.insert(std::make_move_iterator(v.begin()),
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
index b1b33708cde..562ec76e697 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc
@@ -28,34 +28,33 @@
#include <testsuite_hooks.h>
#include <replacement_memory_operators.h>
-static constexpr std::initializer_list<std::pair<const char*, int>> lst = {
- {"long_str_for_dynamic_allocating", 1}
-};
+static constexpr std::initializer_list<std::pair<const char*, const char*>>
lst =
+ { { "long_str_for_dynamic_allocation", "long_str_for_dynamic_allocation" } };
void
test01()
{
__gnu_test::counter::reset();
- std::unordered_multimap<std::string, int,
+ std::unordered_multimap<std::string, std::string,
std::hash<std::string_view>,
std::equal_to<std::string_view>> foo;
foo.insert(lst.begin(), lst.end());
VERIFY( foo.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
}
void
test02()
{
__gnu_test::counter::reset();
- std::unordered_multimap<std::string, int> foo;
+ std::unordered_multimap<std::string, std::string> foo;
foo.insert(lst.begin(), lst.end());
VERIFY( foo.size() == 1 );
- VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::count() == 4 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
}
int
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
index d525bd532be..1efd5be7f10 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc
@@ -28,9 +28,8 @@
#include <testsuite_hooks.h>
#include <replacement_memory_operators.h>
-static constexpr std::initializer_list<const char*> lst = {
- "long_str_for_dynamic_allocating"
-};
+static constexpr std::initializer_list<const char*> lst =
+ { "long_str_for_dynamic_allocation" };
void
test01()
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
index f9ef94eac53..bc2f093f47c 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc
@@ -29,9 +29,8 @@
#include <testsuite_hooks.h>
#include <replacement_memory_operators.h>
-static constexpr std::initializer_list<const char*> lst = {
- "long_str_for_dynamic_allocating"
-};
+static constexpr std::initializer_list<const char*> lst =
+ { "long_str_for_dynamic_allocation" };
void
test01()
@@ -68,7 +67,7 @@ test02()
VERIFY( us.size() == 1 );
VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
}
std::size_t
@@ -126,7 +125,7 @@ test12()
VERIFY( us.size() == 1 );
VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
}
struct hash_string_functor
@@ -186,7 +185,7 @@ test22()
VERIFY( us.size() == 1 );
VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
}
struct hash_string_view_functor
@@ -216,7 +215,7 @@ test23()
VERIFY( us.size() == 1 );
VERIFY( __gnu_test::counter::count() == 3 );
- VERIFY( __gnu_test::counter::get()._M_increments == 3 );
+ VERIFY( __gnu_test::counter::get()._M_increments == 4 );
}
void
@@ -245,7 +244,7 @@ test03()
VERIFY( us.size() == 1 );
VERIFY( __gnu_test::counter::count() == origin + increments );
- VERIFY( __gnu_test::counter::get()._M_increments == increments );
+ VERIFY( __gnu_test::counter::get()._M_increments == increments + 1 );
}
VERIFY( __gnu_test::counter::count() == origin );
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc
b/libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc
new file mode 100644
index 00000000000..6c5cc24930c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc
@@ -0,0 +1,40 @@
+// { dg-do run { target c++11 } }
+
+// libstdc++/115285
+
+#include <string>
+#include <unordered_set>
+
+#include <testsuite_hooks.h>
+
+class TrimmedStr : public std::string
+{
+ static std::string trim_str(std::string const &str)
+ {
+ auto start = str.find_first_not_of(" \r\n\t");
+
+ return start == std::string::npos
+ ? str
+ : str.substr(start, str.find_last_not_of(" \r\n\t") - start + 1);
+ }
+
+public:
+ TrimmedStr(std::string const &arg)
+ : std::string{trim_str(arg)} {}
+ TrimmedStr(char const *arg)
+ : TrimmedStr{std::string{arg}} {}
+};
+
+int main()
+{
+ std::unordered_set<TrimmedStr, std::hash<std::string>,
std::equal_to<std::string>>
+ set_from_initializer_list{ "foo", "bar", " foo ", " bar " };
+
+ VERIFY( set_from_initializer_list.size() == 2 );
+
+ std::vector<std::string> args{ "foo", "bar", " foo ", " bar " };
+ std::unordered_set<TrimmedStr, std::hash<std::string>,
std::equal_to<std::string>>
+ set_from_iterators;
+ set_from_iterators.insert(args.begin(), args.end());
+ VERIFY( set_from_iterators.size() == 2 );
+}