CaseyCarter created this revision.
CaseyCarter added reviewers: mclow.lists, EricWF.
I'm breaking this up into independent bite-sized ~500 line reviews instead of a
single 5200 line monster in hopes that we can turnaround changes quickly.
This first piece implements the new bullet for `common_type`, and all of
`common_reference` and `basic_common_reference`. I've conservatively hidden
everything behind `_LIBCPP_STD_VER > 17`, although it could be made available
in C++11 mode with some edits.
https://reviews.llvm.org/D49118
Files:
include/type_traits
test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp
test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp
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 <class T1, class T2>
using TernaryOp = typename TernaryOpImp<T1, T2>::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<T0>.
+// (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<T0, T0>; otherwise there shall be no member type.
void test_bullet_two() {
static_assert(std::is_same<CommonType<void>, void>::value, "");
static_assert(std::is_same<CommonType<int>, int>::value, "");
@@ -110,11 +113,11 @@
static_assert(std::is_same<CommonType<T, U>, CommonType<DT, DU>>::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<T1> and decay_t<T2>, respectively.
-// (3.3.1)
+// (4.3.1)
// -- If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C
// denote the same type, if any, as common_type_t<D1, D2>.
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<T1> and decay_t<T2>, 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<decltype(false ? declval<D1>() : declval<D2>())>
-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<COND_RES(CREF(D1), CREF(D2))>.
+void test_bullet_three_four() {
+ static_assert(std::is_same_v<CommonType<std::reference_wrapper<int>, 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<T1, T2>. 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
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 <type_traits>
+
+using std::common_reference;
+using std::common_reference_t;
+using std::is_same_v;
+using std::void_t;
+
+template <class, class = void>
+constexpr bool is_trait = false;
+template <class T>
+constexpr bool is_trait<T, void_t<typename T::type>> = true;
+
+// A slightly simplified variation of std::tuple
+template <class...> struct Tuple {};
+
+template <class, class, class> struct Tuple_helper {};
+template <class... Ts, class... Us>
+struct Tuple_helper<void_t<common_reference_t<Ts, Us>...>, Tuple<Ts...>, Tuple<Us...>>
+{
+ using type = Tuple<common_reference_t<Ts, Us>...>;
+};
+
+namespace std
+{
+ template <class... Ts, class... Us,
+ template <class> class TQual, template <class> class UQual>
+ struct basic_common_reference<::Tuple<Ts...>, ::Tuple<Us...>, TQual, UQual>
+ : ::Tuple_helper<void, Tuple<TQual<Ts>...>, Tuple<UQual<Us>...>>
+ {};
+}
+
+struct X2 {};
+struct Y2 {};
+struct Z2 {};
+
+namespace std
+{
+ template <>
+ struct common_type<X2, Y2>
+ {
+ using type = Z2;
+ };
+ template <>
+ struct common_type<Y2, X2>
+ {
+ using type = Z2;
+ };
+}
+
+// (6.1)
+// -- If sizeof...(T) is zero, there shall be no member type.
+void test_bullet_one() {
+ static_assert(!is_trait<common_reference<>>);
+}
+
+// (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<common_reference_t<void>, void>);
+ static_assert(is_same_v<common_reference_t<int>, int>);
+ static_assert(is_same_v<common_reference_t<int&>, int&>);
+ static_assert(is_same_v<common_reference_t<int&&>, int&&>);
+ static_assert(is_same_v<common_reference_t<int const>, int const>);
+ static_assert(is_same_v<common_reference_t<int const&>, int const&>);
+ static_assert(is_same_v<common_reference_t<int const&&>, int const&&>);
+ static_assert(is_same_v<common_reference_t<int volatile[]>, int volatile[]>);
+ static_assert(is_same_v<common_reference_t<int volatile (&)[]>, int volatile (&)[]>);
+ static_assert(is_same_v<common_reference_t<int volatile (&&)[]>, int volatile (&&)[]>);
+ static_assert(is_same_v<common_reference_t<void(&)()>, void(&)()>);
+ static_assert(is_same_v<common_reference_t<void(&&)()>, 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<common_reference_t<B&, D&>, B&>);
+ static_assert(is_same_v<common_reference_t<B const&, D&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B&, D const&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B&, D const&, D&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B&, D&, B&, D&>, B&>);
+
+ static_assert(is_same_v<common_reference_t<B&&, D&&>, B&&>);
+ static_assert(is_same_v<common_reference_t<B const&&, D&&>, B const&&>);
+ static_assert(is_same_v<common_reference_t<B&&, D const&&>, B const&&>);
+ static_assert(is_same_v<common_reference_t<B&, D&&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B&, D const&&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B const&, D&&>, B const&>);
+
+ static_assert(is_same_v<common_reference_t<B&&, D&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B&&, D const&>, B const&>);
+ static_assert(is_same_v<common_reference_t<B const&&, D&>, B const&>);
+ }
+
+ static_assert(is_same_v<
+ common_reference_t<int const volatile&&, int volatile&&>,
+ int const volatile&&>);
+ static_assert(is_same_v<
+ common_reference_t<int&&, int const&, int volatile&>,
+ int const volatile&>);
+
+ static_assert(is_same_v<
+ common_reference_t<int (&)[10], int (&&)[10]>,
+ int const (&)[10]>);
+ static_assert(is_same_v<
+ common_reference_t<int const (&)[10], int volatile (&)[10]>,
+ int const volatile (&)[10]>);
+}
+
+// (6.3.2)
+// -- Otherwise, if basic_common_reference<remove_cvref_t<T1>,
+// remove_cvref_t<T2>, 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<const Tuple<int, short>&, Tuple<int&, short volatile&>>,
+ Tuple<const int&, const volatile short&>>);
+ static_assert(is_same_v<
+ common_reference_t<volatile Tuple<int, short>&, const Tuple<int, short>&>,
+ const volatile Tuple<int, short>&>);
+}
+
+// (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<common_reference_t<void, void>, void>);
+ static_assert(is_same_v<common_reference_t<int, short>, int>);
+ static_assert(is_same_v<common_reference_t<int, short&>, int>);
+ static_assert(is_same_v<common_reference_t<int&, short&>, int>);
+ static_assert(is_same_v<common_reference_t<int&, short>, int>);
+
+ // tricky volatile reference case
+ static_assert(is_same_v<common_reference_t<int&&, int volatile&>, int>);
+ static_assert(is_same_v<common_reference_t<int volatile&, int&&>, int>);
+
+ static_assert(is_same_v<common_reference_t<int (&)[10], int (&)[11]>, int*>);
+
+ {
+ // https://github.com/ericniebler/stl2/issues/338
+ struct MyIntRef { MyIntRef(int&); };
+ static_assert(is_same_v<common_reference_t<int&, MyIntRef>, MyIntRef>);
+ }
+}
+
+// (6.3.4)
+// -- Otherwise, if common_type_t<T1, T2> 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<common_reference_t<moveonly const&, moveonly >, moveonly>);
+ static_assert(is_same_v<common_reference_t<moveonly2 const&, moveonly >, moveonly>);
+ static_assert(is_same_v<common_reference_t<moveonly const&, moveonly2>, moveonly>);
+ }
+
+ static_assert(is_same_v<common_reference_t<X2&, Y2 const&>, Z2>);
+}
+
+// (6.3.5)
+// -- Otherwise, there shall be no member type.
+void test_bullet_three_five() {
+ static_assert(!is_trait<
+ common_reference<volatile Tuple<short>&, const Tuple<int, short>&>>);
+}
+
+// (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<T1, T2>. 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<C, Rest...>.
+void test_bullet_four_one() {
+ static_assert(is_same_v<common_reference_t<int, int, int>, int>);
+ static_assert(is_same_v<
+ common_reference_t<int&&, int const&, int volatile&>,
+ int const volatile&>);
+ static_assert(is_same_v<common_reference_t<int&&, int const&, float&>, float>);
+}
+
+// (6.4.2)
+// -- Otherwise, there shall be no member type.
+void test_bullet_four_two() {
+ static_assert(!is_trait<common_reference<int, short, int, char*>>);
+}
+
+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: include/type_traits
===================================================================
--- include/type_traits
+++ include/type_traits
@@ -350,9 +350,9 @@
template <class T> inline constexpr bool is_nothrow_move_assignable_v
= is_nothrow_move_assignable<T>::value; // C++17
template <class T, class U> inline constexpr bool is_nothrow_swappable_with_v
- = is_nothrow_swappable_with<T, U>::value; // C++17
+ = is_nothrow_swappable_with<T, U>::value; // C++17
template <class T> inline constexpr bool is_nothrow_swappable_v
- = is_nothrow_swappable<T>::value; // C++17
+ = is_nothrow_swappable<T>::value; // C++17
template <class T> inline constexpr bool is_nothrow_destructible_v
= is_nothrow_destructible<T>::value; // C++17
template <class T> inline constexpr bool has_virtual_destructor_v
@@ -395,6 +395,11 @@
template<class B>
inline constexpr bool negation_v = negation<B>::value; // C++17
+ template <class T, class U, template <class> class TQual, template <class> class UQual>
+ struct basic_common_reference { }; // C++20
+ template <class... T> struct common_reference; // C++20
+ template <class... T> // C++20
+ using common_reference_t = typename common_reference<T...>::type; // C++20
}
*/
@@ -414,6 +419,11 @@
template <class>
struct __void_t { typedef void type; };
+#if _LIBCPP_STD_VER > 14
+#define __cpp_lib_void_t 201411
+template <class...> using void_t = void;
+#endif // _LIBCPP_STD_VER > 14
+
template <class _Tp>
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 <class _Tp, class _Up>
+using __cond_res =
+ decltype(false ? _VSTD::declval<_Tp(&)()>()() : _VSTD::declval<_Up(&)()>()());
+
+template <class _Tp, class _Up, class = void>
+struct __common_type3 {};
+
+// sub-bullet 4 - "if COND_RES(CREF(D1), CREF(D2)) denotes a type..."
+template <class _Tp>
+using __cref = add_lvalue_reference_t<const _Tp>;
+
+template <class _Tp, class _Up>
+struct __common_type3<_Tp, _Up, void_t<__cond_res<__cref<_Tp>, __cref<_Up>>>>
+{
+ using type = decay_t<__cond_res<__cref<_Tp>, __cref<_Up>>>;
+};
+
+template <class _Tp, class _Up, class = void>
+struct __common_type2_imp : __common_type3<_Tp, _Up> {};
+#else // ^^^ _LIBCPP_STD_VER > 17 / _LIBCPP_STD_VER <= 17 vvv
template <class _Tp, class _Up, class = void>
struct __common_type2_imp {};
+#endif // _LIBCPP_STD_VER > 17
+
+// sub-bullet 3 - "if decay_t<decltype(false ? declval<D1>() : declval<D2>())> ..."
template <class _Tp, class _Up>
struct __common_type2_imp<_Tp, _Up,
@@ -2112,6 +2147,8 @@
)>::type type;
};
+// sub-bullet 1 - "If is_same_v<T1, D1> is false or ..."
+
template <class _Tp, class _Up,
class _DTp = typename decay<_Tp>::type,
class _DUp = typename decay<_Up>::type>
@@ -2158,7 +2195,174 @@
template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type;
#endif
-#endif // _LIBCPP_HAS_NO_VARIADICS
+#if _LIBCPP_STD_VER > 17
+
+template <class, class, template <class> class, template <class> 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 <class _Tp>
+struct __copy_cv_
+{
+ template <class _Up> using __fn = _Up;
+};
+template <class _Tp>
+struct __copy_cv_<const _Tp>
+{
+ template <class _Up> using __fn = add_const_t<_Up>;
+};
+template <class _Tp>
+struct __copy_cv_<volatile _Tp>
+{
+ template <class _Up> using __fn = add_volatile_t<_Up>;
+};
+template <class _Tp>
+struct __copy_cv_<const volatile _Tp>
+{
+ template <class _Up> using __fn = add_cv_t<_Up>;
+};
+template <class _From, class _To>
+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<A>::template __fn]
+template <class _Tp>
+struct __xref
+{
+ template <class _Up> using __fn = __copy_cv<_Tp, _Up>;
+};
+template <class _Tp>
+struct __xref<_Tp&>
+{
+ template <class _Up> using __fn = add_lvalue_reference_t<__copy_cv<_Tp, _Up>>;
+};
+template <class _Tp>
+struct __xref<_Tp&&>
+{
+ template <class _Up> using __fn = add_rvalue_reference_t<__copy_cv<_Tp, _Up>>;
+};
+
+template <class _Tp, class _Up,
+ class _Result = __cond_res<__copy_cv<_Tp, _Up>&, __copy_cv<_Up, _Tp>&>,
+ class = enable_if_t<is_lvalue_reference_v<_Result>>>
+using __ll_common_ref = _Result;
+
+template <class _Tp, class _Up>
+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 <class...> struct _LIBCPP_TEMPLATE_VIS common_reference {};
+
+template <class... _Types>
+using common_reference_t = typename common_reference<_Types...>::type;
+
+// bullet 2 - sizeof...(T) == 1
+
+template <class _Tp>
+struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp>
+{
+ using type = _Tp;
+};
+
+// bullet 3 - sizeof...(T) == 2
+
+// sub-bullet 4 & 5 - [...] if common_type_t<T1, T2> is well-formed [...]
+// - Otherwise, there shall be no member type.
+
+template <class _Tp, class _Up, class = void>
+struct __common_reference4
+ : common_type<_Tp, _Up>
+{};
+
+// sub-bullet 3 - [...] if COND_RES(T1, T2) is well-formed [...]
+
+template <class _Tp, class _Up>
+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 <class _Tp, class _Up>
+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 <class _Tp, class _Up, class = void>
+struct __common_reference3
+ : __common_reference4<_Tp, _Up>
+{};
+
+template <class _Tp, class _Up>
+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 <class _Tp, class _Up, class = void>
+struct __common_reference2
+ : __common_reference3<_Tp, _Up>
+{};
+
+template <class _Tp, class _Up>
+struct __common_reference2<_Tp&, _Up&, void_t<__ll_common_ref<_Tp, _Up>>>
+{
+ using type = __ll_common_ref<_Tp, _Up>;
+};
+
+template <class _Tp, class _Up>
+struct __common_reference2<_Tp&&, _Up&, enable_if_t<
+ is_convertible_v<_Tp&&, __ll_common_ref<const _Tp, _Up>>>>
+{
+ using type = __ll_common_ref<const _Tp, _Up>;
+};
+
+template <class _Tp, class _Up>
+struct __common_reference2<_Tp&, _Up&&, enable_if_t<
+ is_convertible_v<_Up&&, __ll_common_ref<const _Up, _Tp>>>>
+{
+ using type = __ll_common_ref<const _Up, _Tp>;
+};
+
+template <class _Tp, class _Up>
+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 <class _Tp, class _Up>
+struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up>
+ : __common_reference2<_Tp, _Up>
+{};
+
+// bullet 4 - sizeof...(T) > 2
+
+template <class _Void, class _Tp, class _Up, class... _Types>
+struct __fold_common_reference
+{};
+template <class _Tp, class _Up, class... _Types>
+struct __fold_common_reference<void_t<common_reference_t<_Tp, _Up>>, _Tp, _Up, _Types...>
+ : common_reference<common_reference_t<_Tp, _Up>, _Types...>
+{};
+
+template <class _Tp, class _Up, class _Vp, class... _Rest>
+struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up, _Vp, _Rest...>
+ : __fold_common_reference<void, _Tp, _Up, _Vp, _Rest...>
+{};
+
+#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 <class...> using void_t = void;
-
-# ifndef _LIBCPP_HAS_NO_VARIADICS
+#if _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_VARIADICS)
template <class... _Args>
struct conjunction : __and_<_Args...> {};
template<class... _Args>
@@ -4769,8 +4968,7 @@
template<class _Tp>
_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
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits