@@ -506,4 +506,7 @@ and removed in C++20:
<code class="filename"><cstdalign></code>,
<code class="filename"><cstdbool></code>, and
<code class="filename"><ctgmath></code>.
+</p><p>
+Nested <code class="code">result_type</code> and <code class="code">argument_type</code> removed from
+<code class="classname">std::hash</code> specializations for C++20.
</p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="abi.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="appendix_porting.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="backwards.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ABI Policy and Guidelines </td><td width="20%" align="center"><a accesskey="h" href="../index.html">Home</a></td><td width="40%" align="right" valign="top"> Backwards Compatibility</td></tr></table></div></body></html>
\ No newline at end of file
@@ -1145,6 +1145,11 @@ and removed in C++20:
<filename class="headerfile"><ctgmath></filename>.
</para>
+<para>
+Nested <code>result_type</code> and <code>argument_type</code> removed from
+<classname>std::hash</classname> specializations for C++20.
+</para>
+
</section>
</section>
@@ -52,43 +52,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Result, typename _Arg>
struct __hash_base
{
+#if __cplusplus < 202002L
typedef _Result result_type _GLIBCXX17_DEPRECATED;
typedef _Arg argument_type _GLIBCXX17_DEPRECATED;
+#endif
};
+#if ! _GLIBCXX_INLINE_VERSION
+ // Some std::hash specializations inherit this for ABI compatibility reasons.
+ template<typename _Tp> struct __hash_empty_base { };
+#endif
+
/// Primary class template hash.
template<typename _Tp>
struct hash;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++14-extensions"
template<typename _Tp, typename = void>
- struct __poison_hash
- {
- static constexpr bool __enable_hash_call = false;
- private:
- // Private rather than deleted to be non-trivially-copyable.
- __poison_hash(__poison_hash&&);
- ~__poison_hash();
- };
+ constexpr bool __is_hash_enabled_for = false;
template<typename _Tp>
- struct __poison_hash<_Tp, __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>>
- {
- static constexpr bool __enable_hash_call = true;
- };
+ constexpr bool
+ __is_hash_enabled_for<_Tp,
+ __void_t<decltype(hash<_Tp>()(declval<_Tp>()))>>
+ = true;
+#pragma GCC diagnostic pop
- // Helper struct for SFINAE-poisoning non-enum types.
- template<typename _Tp, bool = is_enum<_Tp>::value>
- struct __hash_enum
+ // Helper struct for defining disabled specializations of std::hash.
+ template<typename _Tp>
+ struct __hash_not_enabled
{
- private:
- // Private rather than deleted to be non-trivially-copyable.
- __hash_enum(__hash_enum&&);
- ~__hash_enum();
+ __hash_not_enabled(__hash_not_enabled&&) = delete;
+ ~__hash_not_enabled() = delete;
};
// Helper struct for hash with enum types.
- template<typename _Tp>
- struct __hash_enum<_Tp, true> : public __hash_base<size_t, _Tp>
+ template<typename _Tp, bool = true>
+ struct __hash_enum : public __hash_base<size_t, _Tp>
{
size_t
operator()(_Tp __val) const noexcept
@@ -99,9 +100,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
};
/// Primary class template hash, usable for enum types only.
- // Use with non-enum types still SFINAES.
template<typename _Tp>
- struct hash : __hash_enum<_Tp>
+ struct hash
+ : __conditional_t<__is_enum(_Tp), __hash_enum<_Tp>, __hash_not_enabled<_Tp>>
{ };
/// Partial specializations for pointer types.
@@ -1012,11 +1012,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// @} relates unique_ptr
/// @cond undocumented
- template<typename _Up, typename _Ptr = typename _Up::pointer,
- bool = __poison_hash<_Ptr>::__enable_hash_call>
+ template<typename _Up, typename _Ptr = typename _Up::pointer>
struct __uniq_ptr_hash
+ : public __hash_base<size_t, _Up>
#if ! _GLIBCXX_INLINE_VERSION
- : private __poison_hash<_Ptr>
+ , private __hash_empty_base<_Ptr>
#endif
{
size_t
@@ -1025,17 +1025,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return hash<_Ptr>()(__u.get()); }
};
- template<typename _Up, typename _Ptr>
- struct __uniq_ptr_hash<_Up, _Ptr, false>
- : private __poison_hash<_Ptr>
- { };
+ template<typename _Up>
+ using __uniq_ptr_hash_base
+ = __conditional_t<__is_hash_enabled_for<typename _Up::pointer>,
+ __uniq_ptr_hash<_Up>,
+ __hash_not_enabled<typename _Up::pointer>>;
/// @endcond
/// std::hash specialization for unique_ptr.
template<typename _Tp, typename _Dp>
struct hash<unique_ptr<_Tp, _Dp>>
- : public __hash_base<size_t, unique_ptr<_Tp, _Dp>>,
- public __uniq_ptr_hash<unique_ptr<_Tp, _Dp>>
+ : public __uniq_ptr_hash_base<unique_ptr<_Tp, _Dp>>
{ };
#ifdef __glibcxx_make_unique // C++ >= 14 && HOSTED
@@ -1732,10 +1732,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Hash.
- template<typename _Tp, typename _Up = remove_const_t<_Tp>,
- bool = __poison_hash<_Up>::__enable_hash_call>
- struct __optional_hash_call_base
+ template<typename _Tp, typename _Up = remove_const_t<_Tp>>
+ struct __optional_hash
+#if ! _GLIBCXX_INLINE_VERSION
+ : public __hash_empty_base<_Up>
+#endif
{
+#if __cplusplus < 202002L
+ using result_type [[__deprecated__]] = size_t;
+ using argument_type [[__deprecated__]] = optional<_Tp>;
+#endif
+
size_t
operator()(const optional<_Tp>& __t) const
noexcept(noexcept(hash<_Up>{}(*__t)))
@@ -1747,17 +1754,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
};
- template<typename _Tp, typename _Up>
- struct __optional_hash_call_base<_Tp, _Up, false> {};
-
template<typename _Tp>
struct hash<optional<_Tp>>
- : private __poison_hash<remove_const_t<_Tp>>,
- public __optional_hash_call_base<_Tp>
- {
- using result_type [[__deprecated__]] = size_t;
- using argument_type [[__deprecated__]] = optional<_Tp>;
- };
+ : public __conditional_t<__is_hash_enabled_for<remove_const_t<_Tp>>,
+ __optional_hash<_Tp>,
+ __hash_not_enabled<_Tp>>
+ { };
template<typename _Tp>
struct __is_fast_hash<hash<optional<_Tp>>> : __is_fast_hash<hash<_Tp>>
@@ -1094,7 +1094,8 @@ namespace __variant
= __gen_vtable_impl<_Array_type, std::index_sequence<>>::_S_apply();
};
- template<size_t _Np, typename _Tp>
+#if ! _GLIBCXX_INLINE_VERSION
+ template<size_t _Nm, typename _Tp>
struct _Base_dedup : public _Tp { };
template<typename _Variant, typename __indices>
@@ -1103,7 +1104,9 @@ namespace __variant
template<typename... _Types, size_t... __indices>
struct _Variant_hash_base<variant<_Types...>,
std::index_sequence<__indices...>>
- : _Base_dedup<__indices, __poison_hash<remove_const_t<_Types>>>... { };
+ : _Base_dedup<__indices, __hash_empty_base<remove_const_t<_Types>>>...
+ { };
+#endif
// Equivalent to decltype(get<_Np>(as-variant(declval<_Variant>())))
template<size_t _Np, typename _Variant,
@@ -1987,9 +1990,14 @@ namespace __detail::__variant
#endif
/// @cond undocumented
- template<bool, typename... _Types>
- struct __variant_hash_call_base_impl
+ template<typename... _Types>
+ struct __variant_hash
{
+#if __cplusplus < 202002L
+ using result_type [[__deprecated__]] = size_t;
+ using argument_type [[__deprecated__]] = variant<_Types...>;
+#endif
+
size_t
operator()(const variant<_Types...>& __t) const
noexcept((is_nothrow_invocable_v<hash<decay_t<_Types>>, _Types> && ...))
@@ -2009,31 +2017,26 @@ namespace __detail::__variant
return __ret;
}
};
-
- 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...>;
/// @endcond
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 [[__deprecated__]] = size_t;
- using argument_type [[__deprecated__]] = variant<_Types...>;
- };
+ : __conditional_t<(__is_hash_enabled_for<remove_const_t<_Types>> && ...),
+ __variant_hash<_Types...>,
+ __hash_not_enabled<variant<_Types...>>>
+#if ! _GLIBCXX_INLINE_VERSION
+ , __detail::__variant::_Variant_hash_base<variant<_Types...>,
+ index_sequence_for<_Types...>>
+#endif
+ { };
template<>
struct hash<monostate>
{
+#if __cplusplus < 202002L
using result_type [[__deprecated__]] = size_t;
using argument_type [[__deprecated__]] = monostate;
+#endif
size_t
operator()(const monostate&) const noexcept
@@ -49,3 +49,36 @@ int main()
std::optional<const int> x3 = x2;
VERIFY(std::hash<int>()(x) == std::hash<std::optional<const int>>()(x3));
}
+
+// Check for presence/absence of nested types.
+
+template<typename T> using res_type = typename std::hash<T>::result_type;
+template<typename T> using arg_type = typename std::hash<T>::argument_type;
+
+template<typename Opt, typename = void>
+constexpr bool has_res_type = false;
+template<typename Opt>
+constexpr bool has_res_type<Opt, std::void_t<res_type<Opt>>> = true;
+template<typename Opt, typename = void>
+constexpr bool has_arg_type = false;
+template<typename Opt>
+constexpr bool has_arg_type<Opt, std::void_t<arg_type<Opt>>> = true;
+
+template<typename T>
+constexpr bool has_no_types
+ = ! has_res_type<std::optional<T>> && ! has_arg_type<std::optional<T>>;
+
+#if __cplusplus >= 202002L
+// Nested types result_type and argument_type are not present in C++20
+static_assert( has_no_types<int> );
+static_assert( has_no_types<double> );
+#else
+// Nested types result_type and argument_type are deprecated in C++17.
+using R1 = std::hash<std::optional<int>>::result_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using A1 = std::hash<std::optional<int>>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using R2 = std::hash<std::optional<char>>::result_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using A2 = std::hash<std::optional<char>>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } }
+#endif
+
+// Disabled specializations do not have the nested types.
+static_assert( has_no_types<S> );
new file mode 100644
@@ -0,0 +1,35 @@
+// { dg-do compile { target c++17 } }
+
+#include <optional>
+
+struct S { }; // std::hash<S> is a disabled specialization.
+
+template<typename T>
+constexpr std::size_t hash_size = sizeof(std::hash<std::optional<T>>);
+
+template<typename... Ts>
+struct MultiHash : std::hash<std::optional<Ts>>...
+{ };
+
+#if _GLIBCXX_INLINE_VERSION
+// For the unstable ABI the size should always be one.
+template<std::size_t Size, typename... Ts>
+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == 1;
+#else
+// For the default ABI, each std::hash<std::optional<T>> specialization has
+// a base class of type __hash_empty_base<remove_cv_t<T>> and if
+// the same type occurs more than once they must have unique addresses.
+template<std::size_t Size, typename... Ts>
+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == Size;
+#endif
+
+static_assert( check_hash_size<1, int> );
+static_assert( check_hash_size<1, int, long, double> );
+static_assert( check_hash_size<2, int, const int> );
+static_assert( check_hash_size<2, int, long, const int> );
+
+static_assert( check_hash_size<1, S> );
+static_assert( check_hash_size<1, int, S> );
+static_assert( check_hash_size<2, int, S, const int> );
+static_assert( check_hash_size<2, int, S, const int, const S> );
+static_assert( check_hash_size<2, int, S, const S, const int> );
new file mode 100644
@@ -0,0 +1,64 @@
+// { dg-do compile { target c++17 } }
+
+#include <memory>
+
+struct S { }; // std::hash<S> is a disabled specialization.
+
+template<typename T, typename D = std::default_delete<T>>
+constexpr std::size_t hash_size = sizeof(std::hash<std::unique_ptr<T, D>>);
+
+template<typename... Ts>
+struct MultiHash : std::hash<std::unique_ptr<Ts>>...
+{ };
+
+#if _GLIBCXX_INLINE_VERSION
+// For the unstable ABI the size should always be one.
+template<std::size_t Size, typename... Ts>
+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == 1;
+#else
+// For the default ABI, each std::hash<std::unique_ptr<T,D >> specialization
+// has a base class of type __hash_empty_base<D::pointer> and if
+// the same type occurs more than once they must have unique addresses.
+template<std::size_t Size, typename... Ts>
+constexpr bool check_hash_size = sizeof(MultiHash<Ts...>) == Size;
+#endif
+
+// All these types have distinct D::pointer types, so no duplicate base classes
+static_assert( check_hash_size<1, int>, "" );
+static_assert( check_hash_size<1, int, long, double>, "" );
+static_assert( check_hash_size<1, int, const int>, "" );
+static_assert( check_hash_size<1, int, long, const int>, "" );
+// Likewise for these disabled specializations:
+static_assert( check_hash_size<1, S>, "" );
+static_assert( check_hash_size<1, int, S>, "" );
+static_assert( check_hash_size<1, int, S, const int>, "" );
+static_assert( check_hash_size<1, int, S, const int, const S>, "" );
+static_assert( check_hash_size<1, int, S, const S, const int>, "" );
+
+// But this has two base classes of type __hash_empty_base<int*>:
+static_assert( check_hash_size<2, int, int[]>, "" );
+static_assert( check_hash_size<2, int, int[], const int>, "" );
+// And this has two base classes of type __hash_not_enabled<S*>:
+static_assert( check_hash_size<2, S, S[]>, "" );
+static_assert( check_hash_size<2, S, S[], const S>, "" );
+
+struct Del : std::default_delete<int> { };
+using P = std::unique_ptr<int>;
+using PD = std::unique_ptr<int, Del>;
+using PC = std::unique_ptr<int, std::default_delete<const int>>;
+using PA = std::unique_ptr<int[]>;
+struct HashClash
+: std::hash<P>, std::hash<PD>, std::hash<PC>, std::hash<PA>
+{ };
+#if _GLIBCXX_INLINE_VERSION
+static_assert(sizeof(HashClash) == 1, "No duplicate bases for unstable ABI");
+#else
+static_assert(sizeof(HashClash) == 4, "four __hash_empty_base<int*> bases");
+#endif
+
+struct Del2 : std::default_delete<const int> { using pointer = const int*; };
+using PD2 = std::unique_ptr<int, Del2>;
+struct Hash2
+: std::hash<PD>, std::hash<PD2>
+{ };
+static_assert(sizeof(Hash2) == 1, "No duplicate bases");
new file mode 100644
@@ -0,0 +1,53 @@
+// { dg-do compile { target c++11 } }
+
+#include <memory>
+
+
+// Check for presence/absence of nested types.
+
+template<typename T> using res_type = typename std::hash<T>::result_type;
+template<typename T> using arg_type = typename std::hash<T>::argument_type;
+
+template<typename UniqPtr, typename = void>
+constexpr bool
+has_res_type(...)
+{ return false; }
+
+template<typename UniqPtr>
+constexpr typename std::is_void<res_type<UniqPtr>>::value_type // i.e. bool
+has_res_type()
+{ return true; }
+
+template<typename UniqPtr, typename = void>
+constexpr bool
+has_arg_type(...)
+{ return false; }
+
+template<typename UniqPtr>
+constexpr typename std::is_void<arg_type<UniqPtr>>::value_type // i.e. bool
+has_arg_type()
+{ return true; }
+
+template<typename UniqPtr>
+constexpr bool
+has_no_types()
+{ return ! has_res_type<UniqPtr>() && ! has_arg_type<UniqPtr>(); }
+
+#if __cplusplus >= 202002L
+// Nested types result_type and argument_type are not present in C++20
+static_assert( has_no_types<std::unique_ptr<int>>() );
+static_assert( has_no_types<std::unique_ptr<double>>() );
+#else
+// Nested types result_type and argument_type are deprecated in C++17.
+using R1 = std::hash<std::unique_ptr<int>>::result_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using A1 = std::hash<std::unique_ptr<int>>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } }
+#endif
+
+struct S { };
+template<> struct std::hash<S*> // disabled specialization
+{
+ hash(hash&&) = delete;
+ ~hash() = delete;
+};
+// Disabled specializations do not have the nested types.
+static_assert( has_no_types<std::unique_ptr<S>>(), "disabled specialization" );
@@ -48,3 +48,37 @@ int main()
std::variant<int> x2 = 42;
VERIFY(std::hash<int>()(x) == std::hash<std::variant<int>>()(x2));
}
+
+// Check for presence/absence of nested types.
+
+template<typename T> using res_type = typename std::hash<T>::result_type;
+template<typename T> using arg_type = typename std::hash<T>::argument_type;
+
+template<typename Variant, typename = void>
+constexpr bool has_res_type = false;
+template<typename Variant>
+constexpr bool has_res_type<Variant, std::void_t<res_type<Variant>>> = true;
+template<typename Variant, typename = void>
+constexpr bool has_arg_type = false;
+template<typename Variant>
+constexpr bool has_arg_type<Variant, std::void_t<arg_type<Variant>>> = true;
+
+template<typename... Ts>
+constexpr bool has_no_types
+ = ! has_res_type<std::variant<Ts...>> && ! has_arg_type<std::variant<Ts...>>;
+
+#if __cplusplus >= 202002L
+// Nested types result_type and argument_type are not present in C++20
+static_assert( has_no_types<int> );
+static_assert( has_no_types<int, double> );
+#else
+// Nested types result_type and argument_type are deprecated in C++17.
+using R1 = std::hash<std::variant<int>>::result_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using A1 = std::hash<std::variant<int>>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using R2 = std::hash<std::variant<char, int>>::result_type; // { dg-warning "deprecated" "" { target c++17_only } }
+using A2 = std::hash<std::variant<char, int>>::argument_type; // { dg-warning "deprecated" "" { target c++17_only } }
+#endif
+
+// Disabled specializations do not have the nested types.
+static_assert( has_no_types<S> );
+static_assert( has_no_types<int, S> );
new file mode 100644
@@ -0,0 +1,48 @@
+// { dg-options "-Wdeprecated" }
+// { dg-do compile { target c++17 } }
+
+#include <variant>
+
+struct S { }; // std::hash<S> is a disabled specialization.
+
+// Test std::hash size
+
+template<typename... Ts>
+constexpr std::size_t hash_size = sizeof(std::hash<std::variant<Ts...>>);
+
+#if _GLIBCXX_INLINE_VERSION
+// For the unstable ABI the size should always be one.
+template<std::size_t Size, typename... Ts>
+constexpr bool check_hash_size = hash_size<Ts...> == 1;
+#else
+// For the default ABI, the std::hash specialization has sizeof...(Ts)
+// base classes of types __hash_empty_base<remove_cv_t<Ts>>... and if
+// the same type occurs more than once they must have unique addresses.
+template<std::size_t Size, typename... Ts>
+constexpr bool check_hash_size = hash_size<Ts...> == Size;
+#endif
+
+static_assert( check_hash_size<1, int> );
+static_assert( check_hash_size<1, int, long, double> );
+static_assert( check_hash_size<2, int, long, int> );
+static_assert( check_hash_size<2, int, long, const int> );
+static_assert( check_hash_size<3, int, int, const int> );
+
+static_assert( check_hash_size<1, S> );
+static_assert( check_hash_size<1, int, S> );
+static_assert( check_hash_size<2, int, S, int> );
+static_assert( check_hash_size<2, int, S, int, S> );
+static_assert( check_hash_size<2, int, S, S, int> );
+static_assert( check_hash_size<3, int, S, S, int, S> );
+
+// For the default ABI this has two __hash_empty_base<int> base classes,
+// for the unstable ABI it does not.
+struct H
+: std::hash<std::variant<int>>, std::hash<std::variant<long, int>>
+{ };
+static_assert( sizeof(H) == hash_size<int, int, long> );
+// Likewise, even though one of the base classes is a disabled specialization.
+struct HX
+: std::hash<std::variant<int>>, std::hash<std::variant<S, int>>
+{ };
+static_assert( sizeof(HX) == hash_size<int, S, int> );