Skip to content

Commit eadece3

Browse files
committed
[libcxx] adds common_reference to <type_traits>
Implements part of P0898R3 Standard Library Concepts Reworks D74351 to use requires-clauses over SFINAE and so that it more closely follows the wording. Co-authored by: Michael Schellenberger Costa <[email protected]> (Michael did all the heavy lifting and I came in to polish it for submission, since Michael is focussing on `std::format` now.) Reviewed By: ldionne, #libc Differential Revision: https://reviews.llvm.org/D96657
1 parent fd82cbc commit eadece3

File tree

2 files changed

+406
-0
lines changed

2 files changed

+406
-0
lines changed

libcxx/include/type_traits

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,6 +2411,216 @@ struct _LIBCPP_TEMPLATE_VIS
24112411
template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type;
24122412
#endif
24132413

2414+
#if _LIBCPP_STD_VER > 11
2415+
// Let COPYCV(FROM, TO) be an alias for type TO with the addition of FROM’s
2416+
// top-level cv-qualifiers.
2417+
template <class _From, class _To>
2418+
struct __copy_cv
2419+
{
2420+
using type = _To;
2421+
};
2422+
2423+
template <class _From, class _To>
2424+
struct __copy_cv<const _From, _To>
2425+
{
2426+
using type = add_const_t<_To>;
2427+
};
2428+
2429+
template <class _From, class _To>
2430+
struct __copy_cv<volatile _From, _To>
2431+
{
2432+
using type = add_volatile_t<_To>;
2433+
};
2434+
2435+
template <class _From, class _To>
2436+
struct __copy_cv<const volatile _From, _To>
2437+
{
2438+
using type = add_cv_t<_To>;
2439+
};
2440+
2441+
template <class _From, class _To>
2442+
using __copy_cv_t = typename __copy_cv<_From, _To>::type;
2443+
2444+
template <class _From, class _To>
2445+
struct __copy_cvref
2446+
{
2447+
using type = __copy_cv_t<_From, _To>;
2448+
};
2449+
2450+
template <class _From, class _To>
2451+
struct __copy_cvref<_From&, _To>
2452+
{
2453+
using type = add_lvalue_reference_t<__copy_cv_t<_From, _To>>;
2454+
};
2455+
2456+
template <class _From, class _To>
2457+
struct __copy_cvref<_From&&, _To>
2458+
{
2459+
using type = add_rvalue_reference_t<__copy_cv_t<_From, _To>>;
2460+
};
2461+
2462+
template <class _From, class _To>
2463+
using __copy_cvref_t = typename __copy_cvref<_From, _To>::type;
2464+
2465+
#endif // _LIBCPP_STD_VER > 11
2466+
2467+
// common_reference
2468+
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
2469+
// Let COND_RES(X, Y) be:
2470+
template <class _Xp, class _Yp>
2471+
using __cond_res =
2472+
decltype(false ? _VSTD::declval<_Xp(&)()>()() : _VSTD::declval<_Yp(&)()>()());
2473+
2474+
// Let `XREF(A)` denote a unary alias template `T` such that `T<U>` denotes the same type as `U`
2475+
// with the addition of `A`'s cv and reference qualifiers, for a non-reference cv-unqualified type
2476+
// `U`.
2477+
// [Note: `XREF(A)` is `__xref<A>::template __apply`]
2478+
template <class _Tp>
2479+
struct __xref {
2480+
template<class _Up>
2481+
using __apply = __copy_cvref_t<_Tp, _Up>;
2482+
};
2483+
2484+
// Given types `A` and `B`, let `X` be `remove_­reference_­t<A>`, let `Y` be `remove_­reference_­t<B>`,
2485+
// and let `COMMON-​REF(A, B)` be:
2486+
template<class _Ap, class _Bp, class _Xp = remove_reference_t<_Ap>, class _Yp = remove_reference_t<_Bp>>
2487+
struct __common_ref;
2488+
2489+
template<class _Xp, class _Yp>
2490+
using __common_ref_t = typename __common_ref<_Xp, _Yp>::__type;
2491+
2492+
template<class _Xp, class _Yp>
2493+
using __cv_cond_res = __cond_res<__copy_cv_t<_Xp, _Yp>&, __copy_cv_t<_Yp, _Xp>&>;
2494+
2495+
2496+
// If `A` and `B` are both lvalue reference types, `COMMON-REF(A, B)` is
2497+
// `COND-RES(COPYCV(X, Y) &, COPYCV(​Y, X) &)` if that type exists and is a reference type.
2498+
template<class _Ap, class _Bp, class _Xp, class _Yp>
2499+
requires requires { typename __cv_cond_res<_Xp, _Yp>; } && is_reference_v<__cv_cond_res<_Xp, _Yp>>
2500+
struct __common_ref<_Ap&, _Bp&, _Xp, _Yp>
2501+
{
2502+
using __type = __cv_cond_res<_Xp, _Yp>;
2503+
};
2504+
2505+
// Otherwise, let `C` be `remove_­reference_­t<COMMON-REF(X&, Y&)>&&`....
2506+
template <class _Xp, class _Yp>
2507+
using __common_ref_C = remove_reference_t<__common_ref_t<_Xp&, _Yp&>>&&;
2508+
2509+
2510+
// .... If `A` and `B` are both rvalue reference types, `C` is well-formed, and
2511+
// `is_­convertible_­v<A, C> && is_­convertible_­v<B, C>` is `true`, then `COMMON-REF(A, B)` is `C`.
2512+
template<class _Ap, class _Bp, class _Xp, class _Yp>
2513+
requires
2514+
requires { typename __common_ref_C<_Xp, _Yp>; } &&
2515+
is_convertible_v<_Ap&&, __common_ref_C<_Xp, _Yp>> &&
2516+
is_convertible_v<_Bp&&, __common_ref_C<_Xp, _Yp>>
2517+
struct __common_ref<_Ap&&, _Bp&&, _Xp, _Yp>
2518+
{
2519+
using __type = __common_ref_C<_Xp, _Yp>;
2520+
};
2521+
2522+
// Otherwise, let `D` be `COMMON-REF(const X&, Y&)`....
2523+
template <class _Tp, class _Up>
2524+
using __common_ref_D = __common_ref_t<const _Tp&, _Up&>;
2525+
2526+
// ... If `A` is an rvalue reference and `B` is an lvalue reference and `D` is well-formed and
2527+
// `is_­convertible_­v<A, D>` is `true`, then `COMMON-REF(A, B)` is `D`.
2528+
template<class _Ap, class _Bp, class _Xp, class _Yp>
2529+
requires requires { typename __common_ref_D<_Xp, _Yp>; } &&
2530+
is_convertible_v<_Ap&&, __common_ref_D<_Xp, _Yp>>
2531+
struct __common_ref<_Ap&&, _Bp&, _Xp, _Yp>
2532+
{
2533+
using __type = __common_ref_D<_Xp, _Yp>;
2534+
};
2535+
2536+
// Otherwise, if `A` is an lvalue reference and `B` is an rvalue reference, then
2537+
// `COMMON-REF(A, B)` is `COMMON-REF(B, A)`.
2538+
template<class _Ap, class _Bp, class _Xp, class _Yp>
2539+
struct __common_ref<_Ap&, _Bp&&, _Xp, _Yp> : __common_ref<_Bp&&, _Ap&> {};
2540+
2541+
// Otherwise, `COMMON-REF(A, B)` is ill-formed.
2542+
template<class _Ap, class _Bp, class _Xp, class _Yp>
2543+
struct __common_ref {};
2544+
2545+
// Note C: For the common_reference trait applied to a parameter pack [...]
2546+
2547+
template <class...>
2548+
struct common_reference;
2549+
2550+
template <class... _Types>
2551+
using common_reference_t = typename common_reference<_Types...>::type;
2552+
2553+
// bullet 1 - sizeof...(T) == 0
2554+
template<>
2555+
struct common_reference<> {};
2556+
2557+
// bullet 2 - sizeof...(T) == 1
2558+
template <class _Tp>
2559+
struct common_reference<_Tp>
2560+
{
2561+
using type = _Tp;
2562+
};
2563+
2564+
// bullet 3 - sizeof...(T) == 2
2565+
template <class _Tp, class _Up> struct __common_reference_sub_bullet3;
2566+
template <class _Tp, class _Up> struct __common_reference_sub_bullet2 : __common_reference_sub_bullet3<_Tp, _Up> {};
2567+
template <class _Tp, class _Up> struct __common_reference_sub_bullet1 : __common_reference_sub_bullet2<_Tp, _Up> {};
2568+
2569+
// sub-bullet 1 - If `T1` and `T2` are reference types and `COMMON-REF(T1, T2)` is well-formed, then
2570+
// the member typedef type denotes that type.
2571+
template <class _Tp, class _Up> struct common_reference<_Tp, _Up> : __common_reference_sub_bullet1<_Tp, _Up> {};
2572+
2573+
template <class _Tp, class _Up>
2574+
requires is_reference_v<_Tp> && is_reference_v<_Up> && requires { typename __common_ref_t<_Tp, _Up>; }
2575+
struct __common_reference_sub_bullet1<_Tp, _Up>
2576+
{
2577+
using type = __common_ref_t<_Tp, _Up>;
2578+
};
2579+
2580+
// sub-bullet 2 - Otherwise, if `basic_­common_­reference<remove_­cvref_­t<T1>, remove_­cvref_­t<T2>, ​XREF(​T1), XREF(T2)>​::​type`
2581+
// is well-formed, then the member typedef type denotes that type.
2582+
template <class, class, template <class> class, template <class> class> struct basic_common_reference {};
2583+
2584+
template <class _Tp, class _Up>
2585+
using __basic_common_reference_t = typename basic_common_reference<
2586+
remove_cvref_t<_Tp>, remove_cvref_t<_Up>,
2587+
__xref<_Tp>::template __apply, __xref<_Up>::template __apply>::type;
2588+
2589+
template <class _Tp, class _Up>
2590+
requires requires { typename __basic_common_reference_t<_Tp, _Up>; }
2591+
struct __common_reference_sub_bullet2<_Tp, _Up>
2592+
{
2593+
using type = __basic_common_reference_t<_Tp, _Up>;
2594+
};
2595+
2596+
// sub-bullet 3 - Otherwise, if `COND-RES(T1, T2)` is well-formed, then the member typedef type
2597+
// denotes that type.
2598+
template <class _Tp, class _Up>
2599+
requires requires { typename __cond_res<_Tp, _Up>; }
2600+
struct __common_reference_sub_bullet3<_Tp, _Up>
2601+
{
2602+
using type = __cond_res<_Tp, _Up>;
2603+
};
2604+
2605+
2606+
// sub-bullet 4 & 5 - Otherwise, if `common_­type_­t<T1, T2>` is well-formed, then the member typedef
2607+
// type denotes that type.
2608+
// - Otherwise, there shall be no member type.
2609+
template <class _Tp, class _Up> struct __common_reference_sub_bullet3 : common_type<_Tp, _Up> {};
2610+
2611+
// bullet 4 - If there is such a type `C`, the member typedef type shall denote the same type, if
2612+
// any, as `common_­reference_­t<C, Rest...>`.
2613+
template <class _Tp, class _Up, class _Vp, class... _Rest>
2614+
requires requires { typename common_reference_t<_Tp, _Up>; }
2615+
struct common_reference<_Tp, _Up, _Vp, _Rest...>
2616+
: common_reference<common_reference_t<_Tp, _Up>, _Vp, _Rest...>
2617+
{};
2618+
2619+
// bullet 5 - Otherwise, there shall be no member type.
2620+
template <class...> struct common_reference {};
2621+
2622+
#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
2623+
24142624
// is_assignable
24152625

24162626
template<typename, typename _Tp> struct __select_2nd { typedef _LIBCPP_NODEBUG_TYPE _Tp type; };

0 commit comments

Comments
 (0)