diff mbox

LWG 2408. SFINAE-friendly common_type/iterator_traits is missing in C++14

Message ID 20141111235804.GP5191@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely Nov. 11, 2014, 11:58 p.m. UTC
This implements LWG 2048. We've been shipping a SFINAE-friendly
iterator_traits in C++11 mode for years, but this adjusts it to match
the form that will be in the next standard i.e. require all the nested
members, not only iterator_category.

To implement this I added __void_t, which is identical to the
std::void_t that's going into C++17 based on N3911, but we can use it
in C++11 mode. I've run a few tests and using __void_t in our
_GLIBCXX_HAS_NESTED_TYPE macro reduces the front-end's memory
footprint and compile-time compared to the old implementation, so I've
altered that. I've also changed a couple of places to use void_t
directly instead of using the _GLIBCXX_HAS_NESTED_TYPE macro at all.

Tested x86_64-linux, committed to trunk.

Comments

Marc Glisse Nov. 12, 2014, 6:13 a.m. UTC | #1
On Tue, 11 Nov 2014, Jonathan Wakely wrote:

> +  template<typename _Iterator, typename = __void_t<>>

Is there a particular reason not to write "void" directly?
Jonathan Wakely Nov. 12, 2014, 10:03 a.m. UTC | #2
On 12/11/14 07:13 +0100, Marc Glisse wrote:
>On Tue, 11 Nov 2014, Jonathan Wakely wrote:
>
>>+  template<typename _Iterator, typename = __void_t<>>
>
>Is there a particular reason not to write "void" directly?

I like this form as a hint that the specialization is expected to use
void_t for that parameter ... but as the specializations tend to be
only about 5 lines away the hint isn't really needed. I'll stop doing
it if you prefer :)
Marc Glisse Nov. 12, 2014, 12:44 p.m. UTC | #3
On Wed, 12 Nov 2014, Jonathan Wakely wrote:

> On 12/11/14 07:13 +0100, Marc Glisse wrote:
>> On Tue, 11 Nov 2014, Jonathan Wakely wrote:
>> 
>>> +  template<typename _Iterator, typename = __void_t<>>
>> 
>> Is there a particular reason not to write "void" directly?
>
> I like this form as a hint that the specialization is expected to use
> void_t for that parameter ... but as the specializations tend to be
> only about 5 lines away the hint isn't really needed. I'll stop doing
> it if you prefer :)

No, documentation is as good a purpose as any, I was just curious.
diff mbox

Patch

commit 57477dc54a9b74a3aaa8d50949d8aa7b23488a4c
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Nov 11 18:55:33 2014 +0000

    Define __void_t and SFINAE-friendly iterator_traits.
    
    	* include/std/type_traits (__void_t): Define new alias template.
    	(_GLIBCXX_HAS_NESTED_TYPE): Redefine using __void_t.
    	* include/std/functional (_Maybe_get_result_type): Likewise.
    	* include/bits/stl_iterator_base_types.h (__iterator_traits): Likewise.
    	* include/bits/uses_allocator.h (__uses_allocator_helper): Likewise.
    	* testsuite/20_util/bind/ref_neg.cc: Adjust dg-error.
    	* testsuite/20_util/reference_wrapper/typedefs-3.cc: Adjust to changes
    	in _GLIBCXX_HAS_NESTED_TYPE.

diff --git a/libstdc++-v3/include/bits/stl_iterator_base_types.h b/libstdc++-v3/include/bits/stl_iterator_base_types.h
index bd55987..ba3f923 100644
--- a/libstdc++-v3/include/bits/stl_iterator_base_types.h
+++ b/libstdc++-v3/include/bits/stl_iterator_base_types.h
@@ -64,7 +64,7 @@ 
 #include <bits/c++config.h>
 
 #if __cplusplus >= 201103L
-# include <type_traits>  // For _GLIBCXX_HAS_NESTED_TYPE, is_convertible
+# include <type_traits>  // For __void_t, is_convertible
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -138,15 +138,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *  provide tighter, more correct semantics.
   */
 #if __cplusplus >= 201103L
-
-_GLIBCXX_HAS_NESTED_TYPE(iterator_category)
-
-  template<typename _Iterator,
-	   bool = __has_iterator_category<_Iterator>::value>
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // 2408. SFINAE-friendly common_type/iterator_traits is missing in C++14
+  template<typename _Iterator, typename = __void_t<>>
     struct __iterator_traits { };
 
   template<typename _Iterator>
-    struct __iterator_traits<_Iterator, true>
+    struct __iterator_traits<_Iterator,
+			     __void_t<typename _Iterator::iterator_category,
+				      typename _Iterator::value_type,
+				      typename _Iterator::difference_type,
+				      typename _Iterator::pointer,
+				      typename _Iterator::reference>>
     {
       typedef typename _Iterator::iterator_category iterator_category;
       typedef typename _Iterator::value_type        value_type;
diff --git a/libstdc++-v3/include/bits/uses_allocator.h b/libstdc++-v3/include/bits/uses_allocator.h
index 7281508..a50ce10 100644
--- a/libstdc++-v3/include/bits/uses_allocator.h
+++ b/libstdc++-v3/include/bits/uses_allocator.h
@@ -40,15 +40,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   constexpr allocator_arg_t allocator_arg = allocator_arg_t();
 
-_GLIBCXX_HAS_NESTED_TYPE(allocator_type)
-
-  template<typename _Tp, typename _Alloc,
-	   bool = __has_allocator_type<_Tp>::value>
+  template<typename _Tp, typename _Alloc, typename = __void_t<>>
     struct __uses_allocator_helper
     : false_type { };
 
   template<typename _Tp, typename _Alloc>
-    struct __uses_allocator_helper<_Tp, _Alloc, true>
+    struct __uses_allocator_helper<_Tp, _Alloc,
+				   __void_t<typename _Tp::allocator_type>>
     : is_convertible<_Alloc, typename _Tp::allocator_type>::type
     { };
 
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 92489b7..e711350 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -67,24 +67,23 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Mem_fn<_Tp _Class::*>
     mem_fn(_Tp _Class::*) noexcept;
 
-_GLIBCXX_HAS_NESTED_TYPE(result_type)
-
   /// If we have found a result_type, extract it.
-  template<bool _Has_result_type, typename _Functor>
+  template<typename _Functor, typename = __void_t<>>
     struct _Maybe_get_result_type
     { };
 
   template<typename _Functor>
-    struct _Maybe_get_result_type<true, _Functor>
+    struct _Maybe_get_result_type<_Functor,
+				  __void_t<typename _Functor::result_type>>
     { typedef typename _Functor::result_type result_type; };
 
   /**
    *  Base class for any function object that has a weak result type, as
-   *  defined in 3.3/3 of TR1.
+   *  defined in 20.8.2 [func.require] of C++11.
   */
   template<typename _Functor>
     struct _Weak_result_type_impl
-    : _Maybe_get_result_type<__has_result_type<_Functor>::value, _Functor>
+    : _Maybe_get_result_type<_Functor>
     { };
 
   /// Retrieve the result type for a function type.
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index cecc7dc..6b72a3a 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2404,34 +2404,22 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     using result_of_t = typename result_of<_Tp>::type;
 #endif
 
+  template<typename...> using __void_t = void;
+
   /// @} group metaprogramming
-	
+
   /**
    *  Use SFINAE to determine if the type _Tp has a publicly-accessible
    *  member type _NTYPE.
    */
 #define _GLIBCXX_HAS_NESTED_TYPE(_NTYPE)				\
-  template<typename _Tp>						\
-    class __has_##_NTYPE##_helper					\
-    {									\
-      template<typename _Up>						\
-	struct _Wrap_type						\
-	{ };								\
-									\
-      template<typename _Up>						\
-	static true_type __test(_Wrap_type<typename _Up::_NTYPE>*);	\
-									\
-      template<typename _Up>						\
-	static false_type __test(...);					\
-									\
-    public:								\
-      typedef decltype(__test<_Tp>(0)) type;				\
-    };									\
-									\
-  template<typename _Tp>						\
+  template<typename _Tp, typename = __void_t<>>				\
     struct __has_##_NTYPE						\
-    : public __has_##_NTYPE##_helper					\
-			<typename remove_cv<_Tp>::type>::type		\
+    : false_type							\
+    { };								\
+  template<typename _Tp>						\
+    struct __has_##_NTYPE<_Tp, __void_t<typename _Tp::_NTYPE>>		\
+    : true_type								\
     { };
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
index 7810968..fde434f 100644
--- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
@@ -30,10 +30,10 @@  void test01()
 {
   const int dummy = 0;
   std::bind(&inc, _1)(0);               // { dg-error  "no match" }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1126 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1140 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1154 }
-  // { dg-error "rvalue|const" "" { target *-*-* } 1168 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1125 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1139 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1153 }
+  // { dg-error "rvalue|const" "" { target *-*-* } 1167 }
   std::bind(&inc, std::ref(dummy))();	// { dg-error  "no match" }
 }
 
diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/typedefs-3.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/typedefs-3.cc
index 7d6a799..909b305 100644
--- a/libstdc++-v3/testsuite/20_util/reference_wrapper/typedefs-3.cc
+++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/typedefs-3.cc
@@ -46,8 +46,7 @@  struct S012 : S0, S1, S2 { };
 
 using std::true_type;
 using std::false_type;
-using std::integral_constant;
-using std::remove_cv;
+using std::__void_t;
 
 _GLIBCXX_HAS_NESTED_TYPE(argument_type)
 _GLIBCXX_HAS_NESTED_TYPE(first_argument_type)