Index: include/type_traits =================================================================== --- include/type_traits +++ include/type_traits @@ -350,9 +350,9 @@ template inline constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable::value; // C++17 template inline constexpr bool is_nothrow_swappable_with_v - = is_nothrow_swappable_with::value; // C++17 + = is_nothrow_swappable_with::value; // C++17 template inline constexpr bool is_nothrow_swappable_v - = is_nothrow_swappable::value; // C++17 + = is_nothrow_swappable::value; // C++17 template inline constexpr bool is_nothrow_destructible_v = is_nothrow_destructible::value; // C++17 template inline constexpr bool has_virtual_destructor_v @@ -395,6 +395,11 @@ template inline constexpr bool negation_v = negation::value; // C++17 + template class TQual, template class UQual> + struct basic_common_reference { }; // C++20 + template struct common_reference; // C++20 + template // C++20 + using common_reference_t = typename common_reference::type; // C++20 } */ @@ -414,6 +419,11 @@ template struct __void_t { typedef void type; }; +#if _LIBCPP_STD_VER > 14 +#define __cpp_lib_void_t 201411 +template using void_t = void; +#endif // _LIBCPP_STD_VER > 14 + template struct __identity { typedef _Tp type; }; @@ -2098,8 +2108,33 @@ // bullet 3 - sizeof...(Tp) == 2 +#if _LIBCPP_STD_VER > 17 +// Let COND_RES(X, Y) be: +template +using __cond_res = + decltype(false ? _VSTD::declval<_Tp(&)()>()() : _VSTD::declval<_Up(&)()>()()); + +template +struct __common_type3 {}; + +// sub-bullet 4 - "if COND_RES(CREF(D1), CREF(D2)) denotes a type..." +template +using __cref = add_lvalue_reference_t; + +template +struct __common_type3<_Tp, _Up, void_t<__cond_res<__cref<_Tp>, __cref<_Up>>>> +{ + using type = decay_t<__cond_res<__cref<_Tp>, __cref<_Up>>>; +}; + +template +struct __common_type2_imp : __common_type3<_Tp, _Up> {}; +#else // ^^^ _LIBCPP_STD_VER > 17 / _LIBCPP_STD_VER <= 17 vvv template struct __common_type2_imp {}; +#endif // _LIBCPP_STD_VER > 17 + +// sub-bullet 3 - "if decay_t() : declval())> ..." template struct __common_type2_imp<_Tp, _Up, @@ -2112,6 +2147,8 @@ )>::type type; }; +// sub-bullet 1 - "If is_same_v is false or ..." + template ::type, class _DUp = typename decay<_Up>::type> @@ -2158,7 +2195,174 @@ template using common_type_t = typename common_type<_Tp...>::type; #endif -#endif // _LIBCPP_HAS_NO_VARIADICS +#if _LIBCPP_STD_VER > 17 + +template class, template class> +struct _LIBCPP_TEMPLATE_VIS basic_common_reference +{}; + +// Let COPYCV(FROM, TO) be an alias for type TO with the addition of FROM’s +// top-level cv-qualifiers. +template +struct __copy_cv_ +{ + template using __fn = _Up; +}; +template +struct __copy_cv_ +{ + template using __fn = add_const_t<_Up>; +}; +template +struct __copy_cv_ +{ + template using __fn = add_volatile_t<_Up>; +}; +template +struct __copy_cv_ +{ + template using __fn = add_cv_t<_Up>; +}; +template +using __copy_cv = typename __copy_cv_<_From>::template __fn<_To>; + +// Let XREF(A) denote a unary class [sic] template T such [...] +// [Note: XREF(A) is __xref::template __fn] +template +struct __xref +{ + template using __fn = __copy_cv<_Tp, _Up>; +}; +template +struct __xref<_Tp&> +{ + template using __fn = add_lvalue_reference_t<__copy_cv<_Tp, _Up>>; +}; +template +struct __xref<_Tp&&> +{ + template using __fn = add_rvalue_reference_t<__copy_cv<_Tp, _Up>>; +}; + +template &, __copy_cv<_Up, _Tp>&>, + class = enable_if_t>> +using __ll_common_ref = _Result; + +template +using __rr_common_ref = remove_reference_t<__ll_common_ref<_Tp, _Up>>&&; + +// Note C: For the common_reference trait applied to a parameter pack [...] + +// bullet 1 - sizeof...(T) == 0 + +template struct _LIBCPP_TEMPLATE_VIS common_reference {}; + +template +using common_reference_t = typename common_reference<_Types...>::type; + +// bullet 2 - sizeof...(T) == 1 + +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp> +{ + using type = _Tp; +}; + +// bullet 3 - sizeof...(T) == 2 + +// sub-bullet 4 & 5 - [...] if common_type_t is well-formed [...] +// - Otherwise, there shall be no member type. + +template +struct __common_reference4 + : common_type<_Tp, _Up> +{}; + +// sub-bullet 3 - [...] if COND_RES(T1, T2) is well-formed [...] + +template +struct __common_reference4<_Tp, _Up, void_t<__cond_res<_Tp, _Up>>> +{ + using type = __cond_res<_Tp, _Up>; +}; + +// sub-bullet 2 - [...] if basic_common_reference<[...]>::type is well-formed [...] + +template +using __basic_common_ref = typename basic_common_reference< + remove_cvref_t<_Tp>, remove_cvref_t<_Up>, + __xref<_Tp>::template __fn, __xref<_Up>::template __fn>::type; + +template +struct __common_reference3 + : __common_reference4<_Tp, _Up> +{}; + +template +struct __common_reference3<_Tp, _Up, void_t<__basic_common_ref<_Tp, _Up>>> +{ + using type = __basic_common_ref<_Tp, _Up>; +}; + +// sub-bullet 1 - If T1 and T2 are reference types and COMMON_REF(T1, T2) is well-formed [...] + +template +struct __common_reference2 + : __common_reference3<_Tp, _Up> +{}; + +template +struct __common_reference2<_Tp&, _Up&, void_t<__ll_common_ref<_Tp, _Up>>> +{ + using type = __ll_common_ref<_Tp, _Up>; +}; + +template +struct __common_reference2<_Tp&&, _Up&, enable_if_t< + is_convertible_v<_Tp&&, __ll_common_ref>>> +{ + using type = __ll_common_ref; +}; + +template +struct __common_reference2<_Tp&, _Up&&, enable_if_t< + is_convertible_v<_Up&&, __ll_common_ref>>> +{ + using type = __ll_common_ref; +}; + +template +struct __common_reference2<_Tp&&, _Up&&, enable_if_t< + is_convertible_v<_Tp&&, __rr_common_ref<_Tp, _Up>> && + is_convertible_v<_Up&&, __rr_common_ref<_Tp, _Up>>>> +{ + using type = __rr_common_ref<_Tp, _Up>; +}; + +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up> + : __common_reference2<_Tp, _Up> +{}; + +// bullet 4 - sizeof...(T) > 2 + +template +struct __fold_common_reference +{}; +template +struct __fold_common_reference>, _Tp, _Up, _Types...> + : common_reference, _Types...> +{}; + +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up, _Vp, _Rest...> + : __fold_common_reference +{}; + +#endif // _LIBCPP_STD_VER > 17 + +#endif // _LIBCPP_HAS_NO_VARIADICS // is_assignable @@ -4746,12 +4950,7 @@ #endif // _LIBCPP_CXX03_LANG -#if _LIBCPP_STD_VER > 14 - -#define __cpp_lib_void_t 201411 -template using void_t = void; - -# ifndef _LIBCPP_HAS_NO_VARIADICS +#if _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_VARIADICS) template struct conjunction : __and_<_Args...> {}; template @@ -4769,8 +4968,7 @@ template _LIBCPP_INLINE_VAR constexpr bool negation_v = negation<_Tp>::value; -# endif // _LIBCPP_HAS_NO_VARIADICS -#endif // _LIBCPP_STD_VER > 14 +#endif // _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_VARIADICS) // These traits are used in __tree and __hash_table #ifndef _LIBCPP_CXX03_LANG Index: test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp =================================================================== --- /dev/null +++ test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 + +// type_traits + +// common_reference + +#include + +using std::common_reference; +using std::common_reference_t; +using std::is_same_v; +using std::void_t; + +template +constexpr bool is_trait = false; +template +constexpr bool is_trait> = true; + +// A slightly simplified variation of std::tuple +template struct Tuple {}; + +template struct Tuple_helper {}; +template +struct Tuple_helper...>, Tuple, Tuple> +{ + using type = Tuple...>; +}; + +namespace std +{ + template class TQual, template class UQual> + struct basic_common_reference<::Tuple, ::Tuple, TQual, UQual> + : ::Tuple_helper...>, Tuple...>> + {}; +} + +struct X2 {}; +struct Y2 {}; +struct Z2 {}; + +namespace std +{ + template <> + struct common_type + { + using type = Z2; + }; + template <> + struct common_type + { + using type = Z2; + }; +} + +// (6.1) +// -- If sizeof...(T) is zero, there shall be no member type. +void test_bullet_one() { + static_assert(!is_trait>); +} + +// (6.2) +// -- Otherwise, if sizeof...(T) is one, let T0 denote the sole type in the +// pack T. The member typedef type shall denote the same type as T0. +void test_bullet_two() { + static_assert(is_same_v, void>); + static_assert(is_same_v, int>); + static_assert(is_same_v, int&>); + static_assert(is_same_v, int&&>); + static_assert(is_same_v, int const>); + static_assert(is_same_v, int const&>); + static_assert(is_same_v, int const&&>); + static_assert(is_same_v, int volatile[]>); + static_assert(is_same_v, int volatile (&)[]>); + static_assert(is_same_v, int volatile (&&)[]>); + static_assert(is_same_v, void(&)()>); + static_assert(is_same_v, void(&&)()>); +} + +// (6.3) +// -- Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in +// the pack T. Then +// (6.3.1) +// -- If T1 and T2 are reference types and COMMON_REF(T1, T2) is well-formed, +// then the member typedef type denotes that type. +void test_bullet_three_one() { + { + struct B {}; + struct D : B {}; + static_assert(is_same_v, B&>); + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B&>); + + static_assert(is_same_v, B&&>); + static_assert(is_same_v, B const&&>); + static_assert(is_same_v, B const&&>); + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B const&>); + + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B const&>); + static_assert(is_same_v, B const&>); + } + + static_assert(is_same_v< + common_reference_t, + int const volatile&&>); + static_assert(is_same_v< + common_reference_t, + int const volatile&>); + + static_assert(is_same_v< + common_reference_t, + int const (&)[10]>); + static_assert(is_same_v< + common_reference_t, + int const volatile (&)[10]>); +} + +// (6.3.2) +// -- Otherwise, if basic_common_reference, +// remove_cvref_t, XREF(T1), XREF(T2)>::type is well-formed, then the +// member typedef type denotes that type. +void test_bullet_three_two() { + static_assert(is_same_v< + common_reference_t&, Tuple>, + Tuple>); + static_assert(is_same_v< + common_reference_t&, const Tuple&>, + const volatile Tuple&>); +} + +// (6.3.3) +// -- Otherwise, if COND_RES(T1, T2) is well-formed, then the member typedef +// type denotes that type. +void test_bullet_three_three() { + static_assert(is_same_v, void>); + static_assert(is_same_v, int>); + static_assert(is_same_v, int>); + static_assert(is_same_v, int>); + static_assert(is_same_v, int>); + + // tricky volatile reference case + static_assert(is_same_v, int>); + static_assert(is_same_v, int>); + + static_assert(is_same_v, int*>); + + { + // https://github.com/ericniebler/stl2/issues/338 + struct MyIntRef { MyIntRef(int&); }; + static_assert(is_same_v, MyIntRef>); + } +} + +// (6.3.4) +// -- Otherwise, if common_type_t is well-formed, then the member +// typedef type denotes that type. +void test_bullet_three_four() { + { + struct moveonly { + moveonly() = default; + moveonly(moveonly&&) = default; + moveonly& operator=(moveonly&&) = default; + }; + struct moveonly2 : moveonly {}; + + static_assert(is_same_v, moveonly>); + static_assert(is_same_v, moveonly>); + static_assert(is_same_v, moveonly>); + } + + static_assert(is_same_v, Z2>); +} + +// (6.3.5) +// -- Otherwise, there shall be no member type. +void test_bullet_three_five() { + static_assert(!is_trait< + common_reference&, const Tuple&>>); +} + +// (6.4) +// -- Otherwise, if sizeof...(T) is greater than two, let T1, T2, and Rest, +// respectively, denote the first, second, and (pack of) remaining types +// comprising T. Let C be the type common_reference_t. Then: +// (6.4.1) +// -- If there is such a type C, the member typedef type shall denote the +// same type, if any, as common_reference_t. +void test_bullet_four_one() { + static_assert(is_same_v, int>); + static_assert(is_same_v< + common_reference_t, + int const volatile&>); + static_assert(is_same_v, float>); +} + +// (6.4.2) +// -- Otherwise, there shall be no member type. +void test_bullet_four_two() { + static_assert(!is_trait>); +} + +int main() { + test_bullet_one(); + test_bullet_two(); + test_bullet_three_one(); + test_bullet_three_two(); + test_bullet_three_three(); + test_bullet_three_four(); + test_bullet_three_five(); + test_bullet_four_one(); + test_bullet_four_two(); +} Index: test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp =================================================================== --- test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp +++ test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp @@ -83,13 +83,16 @@ template using TernaryOp = typename TernaryOpImp::type; +// (4.1) // -- If sizeof...(T) is zero, there shall be no member type. void test_bullet_one() { static_assert(no_common_type<>::value, ""); } -// If sizeof...(T) is one, let T0 denote the sole type constituting the pack T. -// The member typedef-name type shall denote the same type as decay_t. +// (4.2) +// -- If sizeof...(T) is one, let T0 denote the sole type constituting the pack +// T. The member typedef-name type shall denote the same type, if any, as +// common_type_t; otherwise there shall be no member type. void test_bullet_two() { static_assert(std::is_same, void>::value, ""); static_assert(std::is_same, int>::value, ""); @@ -110,11 +113,11 @@ static_assert(std::is_same, CommonType>::value, ""); } -// (3.3) +// (4.3) // -- If sizeof...(T) is two, let the first and second types constituting T be // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types // as decay_t and decay_t, respectively. -// (3.3.1) +// (4.3.1) // -- If is_same_v is false or is_same_v is false, let C // denote the same type, if any, as common_type_t. void test_bullet_three_one() { @@ -148,16 +151,19 @@ } } -// (3.3) +// (4.3) // -- If sizeof...(T) is two, let the first and second types constituting T be // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types // as decay_t and decay_t, respectively. -// (3.3.1) +// (4.3.1) // -- If [...] -// (3.3.2) -// -- Otherwise, let C denote the same type, if any, as +// (4.3.2) +// -- [Note: [...] +// (4.3.3) +// -- Otherwise, if // decay_t() : declval())> -void test_bullet_three_two() { +// denotes a type, let C denote that type. +void test_bullet_three_three() { { using T1 = int const*; using T2 = int*; @@ -188,7 +194,16 @@ } } -// (3.4) +#if TEST_STD_VER > 17 +// (4.3.4) +// -- Otherwise, if COND_RES(CREF(D1), CREF(D2)) denotes a type, let C denote +// the type decay_t. +void test_bullet_three_four() { + static_assert(std::is_same_v, int>, int>); +} +#endif + +// (4.4) // -- If sizeof...(T) is greater than two, let T1, T2, and R, respectively, // denote the first, second, and (pack of) remaining types constituting T. // Let C denote the same type, if any, as common_type_t. If there is @@ -291,7 +306,10 @@ test_bullet_one(); test_bullet_two(); test_bullet_three_one(); - test_bullet_three_two(); + test_bullet_three_three(); +# if TEST_STD_VER > 17 + test_bullet_three_four(); +# endif test_bullet_four(); #endif