[libcxx] Re-implement LWG 2770 again: Fix tuple_size to work with structured bindings

Summary:
This patch attempts to re-implement a fix for LWG 2770, but not the actual specified PR. 

The PR for 2770 specifies tuple_size<T const> as only conditionally providing a `::value` member. However C++17 structured bindings require `tuple_size<T const>` to be complete only if  `tuple_size<T>` is also complete. Therefore this patch implements only provides the specialization `tuple_size<T CV>` iff `tuple_size<T>` is a complete type.

This fixes http://llvm.org/PR31513.

Reviewers: mclow.lists, rsmith, mpark

Subscribers: mpark, cfe-commits

Differential Revision: https://reviews.llvm.org/D28222

llvm-svn: 291019
Cr-Mirrored-From: sso://chromium.googlesource.com/_direct/external/github.com/llvm/llvm-project
Cr-Mirrored-Commit: cb0d4df97490ec2d2b1cdf7574d26b1bc4063599
diff --git a/include/__tuple b/include/__tuple
index 7aad081..ce45db3 100644
--- a/include/__tuple
+++ b/include/__tuple
@@ -24,30 +24,35 @@
 
 template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size;
 
-struct __empty_tuple_size_base {};
-
-template <class _Tp, class = void>
-struct __tuple_size_base_type {
-  typedef __empty_tuple_size_base type;
-};
+#if !defined(_LIBCPP_CXX03_LANG)
+template <class _Tp, class...>
+using __enable_if_tuple_size_imp = _Tp;
 
 template <class _Tp>
-struct __tuple_size_base_type<_Tp, typename __void_t<decltype(tuple_size<_Tp>::value)>::type>
-{
-  typedef integral_constant<size_t, tuple_size<_Tp>::value> type;
-};
+class _LIBCPP_TYPE_VIS_ONLY tuple_size<__enable_if_tuple_size_imp<
+    const _Tp,
+    typename enable_if<!is_volatile<_Tp>::value>::type,
+    integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+    : public integral_constant<size_t, tuple_size<_Tp>::value> {};
 
 template <class _Tp>
-class _LIBCPP_TYPE_VIS_ONLY tuple_size<const _Tp>
-    : public __tuple_size_base_type<_Tp>::type {};
+class _LIBCPP_TYPE_VIS_ONLY tuple_size<__enable_if_tuple_size_imp<
+    volatile _Tp,
+    typename enable_if<!is_const<_Tp>::value>::type,
+    integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+    : public integral_constant<size_t, tuple_size<_Tp>::value> {};
 
 template <class _Tp>
-class _LIBCPP_TYPE_VIS_ONLY tuple_size<volatile _Tp>
-    : public __tuple_size_base_type<_Tp>::type {};
+class _LIBCPP_TYPE_VIS_ONLY tuple_size<__enable_if_tuple_size_imp<
+    const volatile _Tp,
+    integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+    : public integral_constant<size_t, tuple_size<_Tp>::value> {};
 
-template <class _Tp>
-class _LIBCPP_TYPE_VIS_ONLY tuple_size<const volatile _Tp>
-    : public __tuple_size_base_type<_Tp>::type {};
+#else
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size<const _Tp> : tuple_size<_Tp> {};
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size<volatile _Tp> : tuple_size<_Tp> {};
+template <class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_size<const volatile _Tp> : tuple_size<_Tp> {};
+#endif
 
 template <size_t _Ip, class _Tp> class _LIBCPP_TYPE_VIS_ONLY tuple_element;