Commit 509912a6 by Ville Voutilainen Committed by Ville Voutilainen

Make poisoned hashes SFINAE away the call operator of the hash.

* include/bits/functional_hash.h
(__poison_hash::__enable_hash_call): New.
* include/std/optional (__optional_hash_call_base): New.
(hash<optional<_Tp>>): Derive from the new base,
move the hash function into that base.
* include/std/variant (__variant_hash_call_base_impl): New.
(__variant_hash_call_base): Likewise.
(hash<variant<_Types...>>): Derive from the new base,
move the hash function into that base.
* testsuite/20_util/optional/hash.cc: Add tests for is_callable.
* testsuite/20_util/variant/hash.cc: Likewise.

From-SVN: r244748
parent c31d5fce
2017-01-21 Ville Voutilainen <ville.voutilainen@gmail.com>
Make poisoned hashes SFINAE away the call operator of the hash.
* include/bits/functional_hash.h
(__poison_hash::__enable_hash_call): New.
* include/std/optional (__optional_hash_call_base): New.
(hash<optional<_Tp>>): Derive from the new base,
move the hash function into that base.
* include/std/variant (__variant_hash_call_base_impl): New.
(__variant_hash_call_base): Likewise.
(hash<variant<_Types...>>): Derive from the new base,
move the hash function into that base.
* testsuite/20_util/optional/hash.cc: Add tests for is_callable.
* testsuite/20_util/variant/hash.cc: Likewise.
2017-01-20 Joe Seymour <joe.s@somniumtech.com> 2017-01-20 Joe Seymour <joe.s@somniumtech.com>
* acinclude.m4 (GLIBCXX_CHECK_SIZE_T_MANGLING): Support uint20_t. * acinclude.m4 (GLIBCXX_CHECK_SIZE_T_MANGLING): Support uint20_t.
......
...@@ -60,6 +60,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -60,6 +60,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp, typename = void> template<typename _Tp, typename = void>
struct __poison_hash struct __poison_hash
{ {
static constexpr bool __enable_hash_call = false;
private: private:
// Private rather than deleted to be non-trivially-copyable. // Private rather than deleted to be non-trivially-copyable.
__poison_hash(__poison_hash&&); __poison_hash(__poison_hash&&);
...@@ -69,6 +70,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -69,6 +70,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp> template<typename _Tp>
struct __poison_hash<_Tp, __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>> struct __poison_hash<_Tp, __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>>
{ {
static constexpr bool __enable_hash_call = true;
}; };
// Helper struct for SFINAE-poisoning non-enum types. // Helper struct for SFINAE-poisoning non-enum types.
......
...@@ -951,12 +951,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -951,12 +951,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return optional<_Tp> { in_place, __il, std::forward<_Args>(__args)... }; } { return optional<_Tp> { in_place, __il, std::forward<_Args>(__args)... }; }
// Hash. // Hash.
template<typename _Tp>
struct hash<optional<_Tp>> : private __poison_hash<remove_const_t<_Tp>>
{
using result_type = size_t;
using argument_type = optional<_Tp>;
template<typename _Tp, bool
= __poison_hash<remove_const_t<_Tp>>::__enable_hash_call>
struct __optional_hash_call_base
{
size_t size_t
operator()(const optional<_Tp>& __t) const operator()(const optional<_Tp>& __t) const
noexcept(noexcept(hash<_Tp> {}(*__t))) noexcept(noexcept(hash<_Tp> {}(*__t)))
...@@ -968,6 +967,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -968,6 +967,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
}; };
template<typename _Tp>
struct __optional_hash_call_base<_Tp, false> {};
template<typename _Tp>
struct hash<optional<_Tp>>
: private __poison_hash<remove_const_t<_Tp>>,
public __optional_hash_call_base<_Tp>
{
using result_type = size_t;
using argument_type = optional<_Tp>;
};
/// @} /// @}
_GLIBCXX_END_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION
......
...@@ -1273,14 +1273,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1273,14 +1273,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::forward<_Variants>(__variants)...); std::forward<_Variants>(__variants)...);
} }
template<typename... _Types> template<bool, typename... _Types>
struct hash<variant<_Types...>> struct __variant_hash_call_base_impl
: private __detail::__variant::_Variant_hash_base<
variant<_Types...>, std::index_sequence_for<_Types...>>
{ {
using result_type = size_t;
using argument_type = variant<_Types...>;
size_t size_t
operator()(const variant<_Types...>& __t) const operator()(const variant<_Types...>& __t) const
noexcept((is_nothrow_callable_v<hash<decay_t<_Types>>(_Types)> && ...)) noexcept((is_nothrow_callable_v<hash<decay_t<_Types>>(_Types)> && ...))
...@@ -1297,6 +1292,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1297,6 +1292,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
}; };
template<typename... _Types>
struct __variant_hash_call_base_impl<false, _Types...> {};
template<typename... _Types>
using __variant_hash_call_base =
__variant_hash_call_base_impl<(__poison_hash<remove_const_t<_Types>>::
__enable_hash_call &&...), _Types...>;
template<typename... _Types>
struct hash<variant<_Types...>>
: private __detail::__variant::_Variant_hash_base<
variant<_Types...>, std::index_sequence_for<_Types...>>,
public __variant_hash_call_base<_Types...>
{
using result_type = size_t;
using argument_type = variant<_Types...>;
};
template<> template<>
struct hash<monostate> struct hash<monostate>
{ {
......
...@@ -29,6 +29,12 @@ template<class T> ...@@ -29,6 +29,12 @@ template<class T>
auto f(...) -> decltype(std::false_type()); auto f(...) -> decltype(std::false_type());
static_assert(!decltype(f<S>(0))::value, ""); static_assert(!decltype(f<S>(0))::value, "");
static_assert(!std::is_callable<
std::hash<std::optional<S>>&
(std::optional<S> const&)>::value, "");
static_assert(std::is_callable<
std::hash<std::optional<int>>&
(std::optional<int> const&)>::value, "");
int main() int main()
{ {
......
...@@ -33,6 +33,17 @@ static_assert(!decltype(f<std::variant<S>>(0))::value, ""); ...@@ -33,6 +33,17 @@ static_assert(!decltype(f<std::variant<S>>(0))::value, "");
static_assert(!decltype(f<std::variant<S, S>>(0))::value, ""); static_assert(!decltype(f<std::variant<S, S>>(0))::value, "");
static_assert(decltype(f<std::variant<int>>(0))::value, ""); static_assert(decltype(f<std::variant<int>>(0))::value, "");
static_assert(decltype(f<std::variant<int, int>>(0))::value, ""); static_assert(decltype(f<std::variant<int, int>>(0))::value, "");
static_assert(!std::is_callable<
std::hash<std::variant<S>>&(std::variant<S> const&)>::value, "");
static_assert(!std::is_callable<
std::hash<std::variant<S, int>>&
(std::variant<S, int> const&)>::value, "");
static_assert(std::is_callable<
std::hash<std::variant<int>>&
(std::variant<int> const&)>::value, "");
static_assert(std::is_callable<
std::hash<std::variant<int, int>>&
(std::variant<int, int> const&)>::value, "");
int main() int main()
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment