A Readable type has an associated value type that can be accessed with the value_type_t alias template.
template <class> struct value_type { };
template <class T>
struct value_type<T*>
: enable_if<is_object<T>::value, remove_cv_t<T>> { };
template <class I>
requires is_array<I>::value
struct value_type<I> : value_type<decay_t<I>> { };
template <class I>
struct value_type<const I> : value_type<decay_t<I>> { };
template <class T>
requires requires { typename T::value_type; }
struct value_type<T>
: enable_if<is_object<typename T::value_type>::value, typename T::value_type> { };
template <class T>
requires requires { typename T::element_type; }
struct value_type<T>
: enable_if<
is_object<typename T::element_type>::value,
remove_cv_t<typename T::element_type>>
{ };
template <class T> using value_type_t
= typename value_type<T>::type;
If a type I has an associated value type, then value_type<I>::type shall name the value type. Otherwise, there shall be no nested type type.
The value_type class template may be specialized on user-defined types.
When instantiated with a type I such that I::value_type is valid and denotes a type, value_type<I>::type names that type, unless it is not an object type ( ISO/IEC 14882:2014 §[basic.types]) in which case value_type<I> shall have no nested type type. [ Note: Some legacy output iterators define a nested type named value_type that is an alias for void. These types are not Readable and have no associated value types. — end note ]
When instantiated with a type I such that I::element_type is valid and denotes a type, value_type<I>::type names the type remove_cv_t<I::element_type>, unless it is not an object type ( ISO/IEC 14882:2014 §[basic.types]) in which case value_type<I> shall have no nested type type. [ Note: Smart pointers like shared_ptr<int> are Readable and have an associated value type. But a smart pointer like shared_ptr<void> is not Readable and has no associated value type. — end note ]