summaryrefslogtreecommitdiff
path: root/absl/types/optional_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/types/optional_test.cc')
-rw-r--r--absl/types/optional_test.cc50
1 files changed, 50 insertions, 0 deletions
diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc
index 25b44b17..65f43871 100644
--- a/absl/types/optional_test.cc
+++ b/absl/types/optional_test.cc
@@ -24,6 +24,17 @@
#include "absl/meta/type_traits.h"
#include "absl/strings/string_view.h"
+struct Hashable {};
+
+namespace std {
+template <>
+struct hash<Hashable> {
+ size_t operator()(const Hashable&) { return 0; }
+};
+} // namespace std
+
+struct NonHashable {};
+
namespace {
std::string TypeQuals(std::string&) { return "&"; }
@@ -1434,6 +1445,17 @@ TEST(optionalTest, ValueType) {
(std::is_same<absl::optional<int>::value_type, absl::nullopt_t>::value));
}
+template <typename T>
+struct is_hash_enabled_for {
+ template <typename U, typename = decltype(std::hash<U>()(std::declval<U>()))>
+ static std::true_type test(int);
+
+ template <typename U>
+ static std::false_type test(...);
+
+ static constexpr bool value = decltype(test<T>(0))::value;
+};
+
TEST(optionalTest, Hash) {
std::hash<absl::optional<int>> hash;
std::set<size_t> hashcodes;
@@ -1442,6 +1464,34 @@ TEST(optionalTest, Hash) {
hashcodes.insert(hash(i));
}
EXPECT_GT(hashcodes.size(), 90);
+
+ static_assert(is_hash_enabled_for<absl::optional<int>>::value, "");
+ static_assert(is_hash_enabled_for<absl::optional<Hashable>>::value, "");
+
+#if defined(_MSC_VER) || (defined(_LIBCPP_VERSION) && \
+ _LIBCPP_VERSION < 4000 && _LIBCPP_STD_VER > 11)
+ // For MSVC and libc++ (< 4.0 and c++14), std::hash primary template has a
+ // static_assert to catch any user-defined type that doesn't provide a hash
+ // specialization. So instantiating std::hash<absl::optional<T>> will result
+ // in a hard error which is not SFINAE friendly.
+#define ABSL_STD_HASH_NOT_SFINAE_FRIENDLY 1
+#endif
+
+#ifndef ABSL_STD_HASH_NOT_SFINAE_FRIENDLY
+ static_assert(!is_hash_enabled_for<absl::optional<NonHashable>>::value, "");
+#endif
+
+ // libstdc++ std::optional is missing remove_const_t, i.e. it's using
+ // std::hash<T> rather than std::hash<std::remove_const_t<T>>.
+ // Reference: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82262
+#ifndef __GLIBCXX__
+ static_assert(is_hash_enabled_for<absl::optional<const int>>::value, "");
+ static_assert(is_hash_enabled_for<absl::optional<const Hashable>>::value, "");
+ std::hash<absl::optional<const int>> c_hash;
+ for (int i = 0; i < 100; ++i) {
+ EXPECT_EQ(hash(i), c_hash(i));
+ }
+#endif
}
struct MoveMeNoThrow {