diff --git a/include/tl/optional.hpp b/include/tl/optional.hpp index ad8ae2f..157b469 100644 --- a/include/tl/optional.hpp +++ b/include/tl/optional.hpp @@ -2045,11 +2045,36 @@ template class optional { +} // namespace tl + +namespace tl { +namespace detail { +// based off libc++ +// SFINAE guard for std::hash<> specialization +template +using check_hash_requirements = std::integral_constant< + bool, std::is_copy_constructible::value && + std::is_move_constructible::value && + std::is_same>::value>; + +template > +using has_enabled_hash = + std::integral_constant::value && + std::is_default_constructible::value>; + +template using enable_hash_impl = T; + +template +using enable_hash = + enable_hash_impl::value>>; +} // namespace detail } // namespace tl namespace std { -// TODO SFINAE -template struct hash> { +template +struct hash< + tl::detail::enable_hash, tl::detail::remove_const_t>> { ::std::size_t operator()(const tl::optional &o) const { if (!o.has_value()) return 0; diff --git a/tests/hash.cpp b/tests/hash.cpp index af0dff3..6cfe50b 100644 --- a/tests/hash.cpp +++ b/tests/hash.cpp @@ -1,4 +1,30 @@ #include "catch.hpp" #include -TEST_CASE("Hashing", "[hash]") {} +template > +struct is_hashable : std::false_type {}; + +template +struct is_hashable>()( + std::declval()))>> : std::true_type {}; + +template constexpr bool is_hashable_v = is_hashable::value; + +struct not_hashable {}; + +TEST_CASE("Hashing", "[hash]") { + SECTION("with value") { + tl::optional op1(1); + + static_assert(is_hashable_v>, + "tl::optional should be hashable"); + static_assert(!is_hashable_v>, + "tl::optional should not be hashable"); + + REQUIRE(std::hash{}(1) == std::hash>{}(op1)); + } + SECTION("nullopt") { + tl::optional op1(tl::nullopt); + REQUIRE(std::hash>{}(op1) == 0); + } +}