On 08/04/19 19:54 +0100, Jonathan Wakely wrote:
On 08/04/19 17:36 +0100, Jonathan Wakely wrote:
On 08/04/19 19:20 +0300, Ville Voutilainen wrote:
On Mon, 8 Apr 2019 at 19:12, Ville Voutilainen
<ville.voutilai...@gmail.com> wrote:

On Mon, 8 Apr 2019 at 19:02, Jonathan Wakely <jwak...@redhat.com> wrote:
The attached patch implements the same thing with totally separate
__gen_vtable_r and __gen_vtable_r_impl class templates, instead of
adding all the visit<R> functionality into the existing code (and then
needing to tease it apart again with if-constexpr).

The visit<R> implementation doesn't need to care about the
__variant_cookie or __variant_idx_cookie cases, which simplifies
things.

This also adjusts some whitespace, for correct indentation and for
readability. And removes a redundant && from a type.

What do you think?

I hate the duplication of __gen_vtable with a burning passion, because
*that* is the part that causes me heartburn,
not the compile-time ifs in the other bits. But if this is what you
want to ship, I can live with it.

A bit of elaboration: in all of this, the most dreadful parts to
understand were _Multi_array and __gen_vtable.

Completely agreed, that's why I found extending __gen_vtable_impl to
handle a new feature made it even harder to understand.

Whereas the attached patch *removes* code from __gen_vtable, but fixes
the bug in my original std::visit<R> implementation, *and* fixes a bug
in __visitor_result_type (it doesn't use INVOKE).

The downside of this patch is that it fails to diagnose some
ill-formed visitors where not all invocations return the same type. So
it's not acceptable. But I still think there's a simpler design
struggling to get out.

Here's a version that doesn't have that problem.

This adds std::__invoke_r to implement INVOKE<R>, and then uses it in
std::bind, std::function, std::packaged_task and std::visit<R>. Having
a helper for INVOKE<R>  simplifies a number of things, including the
visitation code in <variant>. It also means that we'll only need to
change one place to implement https://wg21.link/p0932r0 once a revised
version of that gets accepted into the draft.

I'll commit this (in a series of patches) in stage 1.


diff --git a/libstdc++-v3/include/bits/invoke.h b/libstdc++-v3/include/bits/invoke.h
index a5278a59f0c..59e22da84d4 100644
--- a/libstdc++-v3/include/bits/invoke.h
+++ b/libstdc++-v3/include/bits/invoke.h
@@ -96,6 +96,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 					std::forward<_Args>(__args)...);
     }
 
+#if __cplusplus >= 201703L
+  // INVOKE<R>: Invoke a callable object and convert the result to R.
+  template<typename _Res, typename _Callable, typename... _Args>
+    constexpr enable_if_t<is_invocable_r_v<_Res, _Callable, _Args...>, _Res>
+    __invoke_r(_Callable&& __fn, _Args&&... __args)
+    noexcept(is_nothrow_invocable_r_v<_Res, _Callable, _Args...>)
+    {
+      using __result = __invoke_result<_Callable, _Args...>;
+      using __type = typename __result::type;
+      using __tag = typename __result::__invoke_type;
+      if constexpr (is_void_v<_Res>)
+	std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
+					std::forward<_Args>(__args)...);
+      else
+	return std::__invoke_impl<__type>(__tag{},
+					  std::forward<_Callable>(__fn),
+					  std::forward<_Args>(__args)...);
+    }
+#else // C++11
+  template<typename _Res, typename _Callable, typename... _Args>
+    using __can_invoke_as_void = __enable_if_t<
+      __and_<is_void<_Res>, __is_invocable<_Callable, _Args...>>::value,
+      _Res
+    >;
+
+  template<typename _Res, typename _Callable, typename... _Args>
+    using __can_invoke_as_nonvoid = __enable_if_t<
+      __and_<__not_<is_void<_Res>>,
+	     is_convertible<typename __invoke_result<_Callable, _Args...>::type,
+			    _Res>
+      >::value,
+      _Res
+    >;
+
+  // INVOKE<R>: Invoke a callable object and convert the result to R.
+  template<typename _Res, typename _Callable, typename... _Args>
+    constexpr __can_invoke_as_nonvoid<_Res, _Callable, _Args...>
+    __invoke_r(_Callable&& __fn, _Args&&... __args)
+    {
+      using __result = __invoke_result<_Callable, _Args...>;
+      using __type = typename __result::type;
+      using __tag = typename __result::__invoke_type;
+      return std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
+					std::forward<_Args>(__args)...);
+    }
+
+  // INVOKE<R> when R is cv void
+  template<typename _Res, typename _Callable, typename... _Args>
+    constexpr __can_invoke_as_void<_Res, _Callable, _Args...>
+    __invoke_r(_Callable&& __fn, _Args&&... __args)
+    {
+      using __result = __invoke_result<_Callable, _Args...>;
+      using __type = typename __result::type;
+      using __tag = typename __result::__invoke_type;
+      std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
+				 std::forward<_Args>(__args)...);
+    }
+#endif // C++11
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
diff --git a/libstdc++-v3/include/bits/std_function.h b/libstdc++-v3/include/bits/std_function.h
index b70ed564d11..5733bf5f3f9 100644
--- a/libstdc++-v3/include/bits/std_function.h
+++ b/libstdc++-v3/include/bits/std_function.h
@@ -109,21 +109,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     __destroy_functor
   };
 
-  // Simple type wrapper that helps avoid annoying const problems
-  // when casting between void pointers and pointers-to-pointers.
-  template<typename _Tp>
-    struct _Simple_type_wrapper
-    {
-      _Simple_type_wrapper(_Tp __value) : __value(__value) { }
-
-      _Tp __value;
-    };
-
-  template<typename _Tp>
-    struct __is_location_invariant<_Simple_type_wrapper<_Tp> >
-    : __is_location_invariant<_Tp>
-    { };
-
   template<typename _Signature>
     class function;
 
@@ -278,56 +263,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       typedef _Function_base::_Base_manager<_Functor> _Base;
 
-    public:
-      static _Res
-      _M_invoke(const _Any_data& __functor, _ArgTypes&&... __args)
-      {
-	return (*_Base::_M_get_pointer(__functor))(
-	    std::forward<_ArgTypes>(__args)...);
-      }
-    };
-
-  template<typename _Functor, typename... _ArgTypes>
-    class _Function_handler<void(_ArgTypes...), _Functor>
-    : public _Function_base::_Base_manager<_Functor>
-    {
-      typedef _Function_base::_Base_manager<_Functor> _Base;
-
-     public:
-      static void
-      _M_invoke(const _Any_data& __functor, _ArgTypes&&... __args)
-      {
-	(*_Base::_M_get_pointer(__functor))(
-	    std::forward<_ArgTypes>(__args)...);
-      }
-    };
-
-  template<typename _Class, typename _Member, typename _Res,
-	   typename... _ArgTypes>
-    class _Function_handler<_Res(_ArgTypes...), _Member _Class::*>
-    : public _Function_handler<void(_ArgTypes...), _Member _Class::*>
-    {
-      typedef _Function_handler<void(_ArgTypes...), _Member _Class::*>
-	_Base;
-
-     public:
-      static _Res
-      _M_invoke(const _Any_data& __functor, _ArgTypes&&... __args)
-      {
-	return std::__invoke(_Base::_M_get_pointer(__functor)->__value,
-			     std::forward<_ArgTypes>(__args)...);
-      }
-    };
-
-  template<typename _Class, typename _Member, typename... _ArgTypes>
-    class _Function_handler<void(_ArgTypes...), _Member _Class::*>
-    : public _Function_base::_Base_manager<
-		 _Simple_type_wrapper< _Member _Class::* > >
-    {
-      typedef _Member _Class::* _Functor;
-      typedef _Simple_type_wrapper<_Functor> _Wrapper;
-      typedef _Function_base::_Base_manager<_Wrapper> _Base;
-
     public:
       static bool
       _M_manager(_Any_data& __dest, const _Any_data& __source,
@@ -341,8 +276,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    break;
 #endif
 	  case __get_functor_ptr:
-	    __dest._M_access<_Functor*>() =
-	      &_Base::_M_get_pointer(__source)->__value;
+	    __dest._M_access<_Functor*>() = _Base::_M_get_pointer(__source);
 	    break;
 
 	  default:
@@ -351,11 +285,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return false;
       }
 
-      static void
+      static _Res
       _M_invoke(const _Any_data& __functor, _ArgTypes&&... __args)
       {
-	std::__invoke(_Base::_M_get_pointer(__functor)->__value,
-		      std::forward<_ArgTypes>(__args)...);
+	return std::__invoke_r<_Res>(*_Base::_M_get_pointer(__functor),
+				     std::forward<_ArgTypes>(__args)...);
       }
     };
 
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 8cf2c670648..1834ed8ca4d 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -550,78 +550,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       // Call unqualified
       template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__disable_if_void<_Res>
+	_Res
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>)
 	{
-	  return std::__invoke(_M_f, _Mu<_Bound_args>()
+	  return std::__invoke_r<_Res>(_M_f, _Mu<_Bound_args>()
 		      (std::get<_Indexes>(_M_bound_args), __args)...);
 	}
 
-      // Call unqualified, return void
-      template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__enable_if_void<_Res>
-	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>)
-	{
-	  std::__invoke(_M_f, _Mu<_Bound_args>()
-	       (std::get<_Indexes>(_M_bound_args), __args)...);
-	}
-
       // Call as const
       template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__disable_if_void<_Res>
+	_Res
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) const
 	{
-	  return std::__invoke(_M_f, _Mu<_Bound_args>()
+	  return std::__invoke_r<_Res>(_M_f, _Mu<_Bound_args>()
 		      (std::get<_Indexes>(_M_bound_args), __args)...);
 	}
 
-      // Call as const, return void
-      template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__enable_if_void<_Res>
-	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) const
-	{
-	  std::__invoke(_M_f, _Mu<_Bound_args>()
-	       (std::get<_Indexes>(_M_bound_args),  __args)...);
-	}
-
       // Call as volatile
       template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__disable_if_void<_Res>
+	_Res
 	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) volatile
 	{
-	  return std::__invoke(_M_f, _Mu<_Bound_args>()
+	  return std::__invoke_r<_Res>(_M_f, _Mu<_Bound_args>()
 		      (__volget<_Indexes>(_M_bound_args), __args)...);
 	}
 
-      // Call as volatile, return void
-      template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__enable_if_void<_Res>
-	__call(tuple<_Args...>&& __args, _Index_tuple<_Indexes...>) volatile
-	{
-	  std::__invoke(_M_f, _Mu<_Bound_args>()
-	       (__volget<_Indexes>(_M_bound_args), __args)...);
-	}
-
       // Call as const volatile
       template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__disable_if_void<_Res>
+	_Res
 	__call(tuple<_Args...>&& __args,
 	       _Index_tuple<_Indexes...>) const volatile
 	{
-	  return std::__invoke(_M_f, _Mu<_Bound_args>()
+	  return std::__invoke_r<_Res>(_M_f, _Mu<_Bound_args>()
 		      (__volget<_Indexes>(_M_bound_args), __args)...);
 	}
 
-      // Call as const volatile, return void
-      template<typename _Res, typename... _Args, std::size_t... _Indexes>
-	__enable_if_void<_Res>
-	__call(tuple<_Args...>&& __args,
-	       _Index_tuple<_Indexes...>) const volatile
-	{
-	  std::__invoke(_M_f, _Mu<_Bound_args>()
-	       (__volget<_Indexes>(_M_bound_args), __args)...);
-	}
-
     public:
       typedef _Result result_type;
 
diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
index 10136e57a84..967110050b8 100644
--- a/libstdc++-v3/include/std/future
+++ b/libstdc++-v3/include/std/future
@@ -1417,8 +1417,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       virtual void
       _M_run(_Args&&... __args)
       {
-	auto __boundfn = [&] () -> typename result_of<_Fn&(_Args&&...)>::type {
-	    return std::__invoke(_M_impl._M_fn, std::forward<_Args>(__args)...);
+	auto __boundfn = [&] () -> _Res {
+	    return std::__invoke_r<_Res>(_M_impl._M_fn,
+					 std::forward<_Args>(__args)...);
 	};
 	this->_M_set_result(_S_task_setter(this->_M_result, __boundfn));
       }
@@ -1426,8 +1427,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       virtual void
       _M_run_delayed(_Args&&... __args, weak_ptr<_State_base> __self)
       {
-	auto __boundfn = [&] () -> typename result_of<_Fn&(_Args&&...)>::type {
-	    return std::__invoke(_M_impl._M_fn, std::forward<_Args>(__args)...);
+	auto __boundfn = [&] () -> _Res {
+	    return std::__invoke_r<_Res>(_M_impl._M_fn,
+					 std::forward<_Args>(__args)...);
 	};
 	this->_M_set_delayed_result(_S_task_setter(this->_M_result, __boundfn),
 				    std::move(__self));
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index c3cb67a457d..a1651650020 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -1378,7 +1378,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : public __is_convertible_helper<_From, _To>::type
     { };
 
-#if __cplusplus > 201703L
     template<typename _From, typename _To,
            bool = __or_<is_void<_From>, is_function<_To>,
                         is_array<_To>>::value>
@@ -1393,7 +1392,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	static void __test_aux(_To1) noexcept;
 
       template<typename _From1, typename _To1>
-	static bool_constant<noexcept(__test_aux<_To1>(std::declval<_From1>()))>
+	static
+	__bool_constant<noexcept(__test_aux<_To1>(std::declval<_From1>()))>
 	__test(int);
 
       template<typename, typename>
@@ -1404,6 +1404,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using type = decltype(__test<_From, _To>(0));
     };
 
+  // is_nothrow_convertible for C++11
+  template<typename _From, typename _To>
+    struct __is_nothrow_convertible
+    : public __is_nt_convertible_helper<_From, _To>::type
+    { };
+
+#if __cplusplus > 201703L
   /// is_nothrow_convertible
   template<typename _From, typename _To>
     struct is_nothrow_convertible
@@ -2816,8 +2823,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __is_nt_invocable_impl<_Result, _Ret,
 				  __void_t<typename _Result::type>>
     : __or_<is_void<_Ret>,
-	    __and_<is_convertible<typename _Result::type, _Ret>,
-		   is_nothrow_constructible<_Ret, typename _Result::type>>>
+	    __is_nothrow_convertible<typename _Result::type, _Ret>>
     { };
 
   /// std::is_nothrow_invocable_r
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 22b0c3d5c22..e38830d656d 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -138,9 +138,7 @@ namespace __variant
     constexpr variant_alternative_t<_Np, variant<_Types...>> const&&
     get(const variant<_Types...>&&);
 
-  template<bool __use_index=false,
-	   bool __same_return_types = true,
-	   typename _Visitor, typename... _Variants>
+  template<typename _Result_type, typename _Visitor, typename... _Variants>
     constexpr decltype(auto)
     __do_visit(_Visitor&& __visitor, _Variants&&... __variants);
 
@@ -180,8 +178,26 @@ namespace __variant
   struct __variant_cookie {};
   // used for raw visitation with indices passed in
   struct __variant_idx_cookie { using type = __variant_idx_cookie; };
-  // a more explanatory name than 'true'
-  inline constexpr auto __visit_with_index = bool_constant<true>{};
+  // Used to enable deduction (and same-type checking) for std::visit:
+  template<typename> struct __deduce_visit_result { };
+
+  // Visit variants that might be valueless.
+  template<typename _Visitor, typename... _Variants>
+    constexpr void
+    __raw_visit(_Visitor&& __visitor, _Variants&&... __variants)
+    {
+      std::__do_visit<__variant_cookie>(std::forward<_Visitor>(__visitor),
+				        std::forward<_Variants>(__variants)...);
+    }
+
+  // Visit variants that might be valueless, passing indices to the visitor.
+  template<typename _Visitor, typename... _Variants>
+    constexpr void
+    __raw_idx_visit(_Visitor&& __visitor, _Variants&&... __variants)
+    {
+      std::__do_visit<__variant_idx_cookie>(std::forward<_Visitor>(__visitor),
+	  std::forward<_Variants>(__variants)...);
+    }
 
   // _Uninitialized<T> is guaranteed to be a literal type, even if T is not.
   // We have to do this, because [basic.types]p10.5.3 (n4606) is not implemented
@@ -381,13 +397,11 @@ namespace __variant
 
       constexpr void _M_reset_impl()
       {
-	__do_visit([](auto&& __this_mem) mutable
-		   -> __detail::__variant::__variant_cookie
+	__variant::__raw_visit([](auto&& __this_mem) mutable
 	  {
 	    if constexpr (!is_same_v<remove_reference_t<decltype(__this_mem)>,
 			  __variant_cookie>)
 	      std::_Destroy(std::__addressof(__this_mem));
-	    return {};
 	  }, __variant_cast<_Types...>(*this));
       }
 
@@ -470,12 +484,10 @@ namespace __variant
     void __variant_construct(_Tp&& __lhs, _Up&& __rhs)
     {
       __lhs._M_index = __rhs._M_index;
-      __do_visit([&__lhs](auto&& __rhs_mem) mutable
-		 -> __detail::__variant::__variant_cookie
+      __variant::__raw_visit([&__lhs](auto&& __rhs_mem) mutable
         {
 	  __variant_construct_single(std::forward<_Tp>(__lhs),
 	      std::forward<decltype(__rhs_mem)>( __rhs_mem));
-	  return {};
 	}, __variant_cast<_Types...>(std::forward<decltype(__rhs)>(__rhs)));
     }
 
@@ -599,9 +611,8 @@ namespace __variant
       operator=(const _Copy_assign_base& __rhs)
 	  noexcept(_Traits<_Types...>::_S_nothrow_copy_assign)
       {
-	__do_visit<__visit_with_index>([this](auto&& __rhs_mem,
-					      auto __rhs_index) mutable
-	    -> __detail::__variant::__variant_idx_cookie
+	__variant::__raw_idx_visit(
+	  [this](auto&& __rhs_mem, auto __rhs_index) mutable
 	  {
 	    if constexpr (__rhs_index != variant_npos)
 	      {
@@ -635,7 +646,6 @@ namespace __variant
 	      }
 	    else
 	      this->_M_reset();
-	    return {};
 	  }, __variant_cast<_Types...>(__rhs));
 	return *this;
       }
@@ -668,9 +678,8 @@ namespace __variant
       operator=(_Move_assign_base&& __rhs)
 	  noexcept(_Traits<_Types...>::_S_nothrow_move_assign)
       {
-	__do_visit<__visit_with_index>([this](auto&& __rhs_mem,
-					      auto __rhs_index) mutable
-	    -> __detail::__variant::__variant_idx_cookie
+	__variant::__raw_idx_visit(
+	  [this](auto&& __rhs_mem, auto __rhs_index) mutable
 	  {
 	    if constexpr (__rhs_index != variant_npos)
 	      {
@@ -692,7 +701,6 @@ namespace __variant
 	      }
 	    else
 	      this->_M_reset();
-	    return {};
 	  }, __variant_cast<_Types...>(__rhs));
 	return *this;
       }
@@ -822,11 +830,38 @@ namespace __variant
   template<typename _Tp>
     struct _Multi_array<_Tp>
     {
-      constexpr const _Tp&
+      template<typename>
+	struct __untag_result
+	: false_type
+	{ using element_type = _Tp; };
+
+      template <typename... _Args>
+	struct __untag_result<const void(*)(_Args...)>
+	: false_type
+	{ using element_type = void(*)(_Args...); };
+
+      template <typename... _Args>
+	struct __untag_result<__variant_cookie(*)(_Args...)>
+	: false_type
+	{ using element_type = void(*)(_Args...); };
+
+      template <typename... _Args>
+	struct __untag_result<__variant_idx_cookie(*)(_Args...)>
+	: false_type
+	{ using element_type = void(*)(_Args...); };
+
+      template <typename _Res, typename... _Args>
+	struct __untag_result<__deduce_visit_result<_Res>(*)(_Args...)>
+	: true_type
+	{ using element_type = _Res(*)(_Args...); };
+
+      using __result_is_deduced = __untag_result<_Tp>;
+
+      constexpr const typename __untag_result<_Tp>::element_type&
       _M_access() const
       { return _M_data; }
 
-      _Tp _M_data;
+      typename __untag_result<_Tp>::element_type _M_data;
     };
 
   // Partial specialization with rank >= 1.
@@ -847,7 +882,7 @@ namespace __variant
       using _Tp = _Ret(*)(_Visitor, _Variants...);
 
       template<typename... _Args>
-	constexpr const _Tp&
+	constexpr decltype(auto)
 	_M_access(size_t __first_index, _Args... __rest_indices) const
         {
 	  return _M_arr[__first_index + __do_cookie]
@@ -859,37 +894,32 @@ namespace __variant
 
   // Creates a multi-dimensional vtable recursively.
   //
-  // The __same_return_types non-type template parameter specifies whether
-  // to enforce that all visitor invocations return the same type. This is
-  // required by std::visit but not std::visit<R>.
-  //
   // For example,
   // visit([](auto, auto){},
   //       variant<int, char>(),  // typedef'ed as V1
   //       variant<float, double, long double>())  // typedef'ed as V2
   // will trigger instantiations of:
-  // __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&), 2, 3>,
+  // __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 2, 3>,
   //                   tuple<V1&&, V2&&>, std::index_sequence<>>
-  //   __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&), 3>,
+  //   __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>,
   //                     tuple<V1&&, V2&&>, std::index_sequence<0>>
-  //     __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&)>,
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
   //                       tuple<V1&&, V2&&>, std::index_sequence<0, 0>>
-  //     __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&)>,
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
   //                       tuple<V1&&, V2&&>, std::index_sequence<0, 1>>
-  //     __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&)>,
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
   //                       tuple<V1&&, V2&&>, std::index_sequence<0, 2>>
-  //   __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&), 3>,
+  //   __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&), 3>,
   //                     tuple<V1&&, V2&&>, std::index_sequence<1>>
-  //     __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&)>,
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
   //                       tuple<V1&&, V2&&>, std::index_sequence<1, 0>>
-  //     __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&)>,
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
   //                       tuple<V1&&, V2&&>, std::index_sequence<1, 1>>
-  //     __gen_vtable_impl<true, _Multi_array<void(*)(V1&&, V2&&)>,
+  //     __gen_vtable_impl<_Multi_array<void(*)(V1&&, V2&&)>,
   //                       tuple<V1&&, V2&&>, std::index_sequence<1, 2>>
   // The returned multi-dimensional vtable can be fast accessed by the visitor
   // using index calculation.
-  template<bool __same_return_types,
-	   typename _Array_type, typename _Variant_tuple, typename _Index_seq>
+  template<typename _Array_type, typename _Index_seq>
     struct __gen_vtable_impl;
 
   // Defines the _S_apply() member that returns a _Multi_array populated
@@ -899,13 +929,11 @@ namespace __variant
   // This partial specialization builds up the index sequences by recursively
   // calling _S_apply() on the next specialization of __gen_vtable_impl.
   // The base case of the recursion defines the actual function pointers.
-  template<bool __same_return_types,
-	   typename _Result_type, typename _Visitor, size_t... __dimensions,
+  template<typename _Result_type, typename _Visitor, size_t... __dimensions,
 	   typename... _Variants, size_t... __indices>
     struct __gen_vtable_impl<
-        __same_return_types,
 	_Multi_array<_Result_type (*)(_Visitor, _Variants...), __dimensions...>,
-	tuple<_Variants...>, std::index_sequence<__indices...>>
+	std::index_sequence<__indices...>>
     {
       using _Next =
 	  remove_reference_t<typename _Nth_type<sizeof...(__indices),
@@ -941,25 +969,19 @@ namespace __variant
 	static constexpr void
 	_S_apply_single_alt(_Tp& __element, _Tp* __cookie_element = nullptr)
 	{
-	  using _Alternative = variant_alternative_t<__index, _Next>;
 	  if constexpr (__do_cookie)
 	    {
 	      __element = __gen_vtable_impl<
-		__same_return_types,
 		_Tp,
-		tuple<_Variants...>,
 		std::index_sequence<__indices..., __index>>::_S_apply();
 	      *__cookie_element = __gen_vtable_impl<
-		__same_return_types,
 		_Tp,
-		tuple<_Variants...>,
 		std::index_sequence<__indices..., variant_npos>>::_S_apply();
 	    }
 	  else
 	    {
 	      __element = __gen_vtable_impl<
-		__same_return_types,
-		remove_reference_t<decltype(__element)>, tuple<_Variants...>,
+		remove_reference_t<decltype(__element)>,
 		std::index_sequence<__indices..., __index>>::_S_apply();
 	    }
 	}
@@ -968,13 +990,11 @@ namespace __variant
   // This partial specialization is the base case for the recursion.
   // It populates a _Multi_array element with the address of a function
   // that invokes the visitor with the alternatives specified by __indices.
-  template<bool __same_return_types,
-	   typename _Result_type, typename _Visitor, typename... _Variants,
+  template<typename _Result_type, typename _Visitor, typename... _Variants,
 	   size_t... __indices>
     struct __gen_vtable_impl<
-      __same_return_types,
       _Multi_array<_Result_type (*)(_Visitor, _Variants...)>,
-		   tuple<_Variants...>, std::index_sequence<__indices...>>
+		   std::index_sequence<__indices...>>
     {
       using _Array_type =
 	  _Multi_array<_Result_type (*)(_Visitor, _Variants...)>;
@@ -989,51 +1009,38 @@ namespace __variant
 	    return __variant_cookie{};
 	}
 
+      // Perform the implicit conversion to _Result_type for std::visit<R>.
+      static constexpr _Result_type
+      __do_visit_invoke_r(_Visitor&& __visitor, _Variants... __vars)
+      {
+	return std::__invoke(std::forward<_Visitor>(__visitor),
+	      __variant::__get<__indices>(std::forward<_Variants>(__vars))...);
+      }
+
       static constexpr decltype(auto)
-      __visit_invoke_impl(_Visitor&& __visitor, _Variants... __vars)
+      __visit_invoke(_Visitor&& __visitor, _Variants... __vars)
       {
-	// For raw visitation using indices, pass the indices to the visitor:
 	if constexpr (is_same_v<_Result_type, __variant_idx_cookie>)
-	  return std::__invoke(std::forward<_Visitor>(__visitor),
+	  // For raw visitation using indices, pass the indices to the visitor
+	  // and discard the return value:
+	  std::__invoke(std::forward<_Visitor>(__visitor),
 	      __element_by_index_or_cookie<__indices>(
 		std::forward<_Variants>(__vars))...,
 	      integral_constant<size_t, __indices>()...);
-	// For std::visit<cv void>, cast the result to void:
-	else if constexpr (!__same_return_types &&
-			   std::is_void_v<_Result_type>)
-	  return (void)std::__invoke(std::forward<_Visitor>(__visitor),
+	else if constexpr (is_same_v<_Result_type, __variant_cookie>)
+	  // For raw visitation without indices, and discard the return value:
+	  std::__invoke(std::forward<_Visitor>(__visitor),
 	      __element_by_index_or_cookie<__indices>(
 		std::forward<_Variants>(__vars))...);
-	else
+	else if constexpr (_Array_type::__result_is_deduced::value)
+	  // For the usual std::visit case deduce the return value:
 	  return std::__invoke(std::forward<_Visitor>(__visitor),
 	      __element_by_index_or_cookie<__indices>(
 		std::forward<_Variants>(__vars))...);
-      }
-
-      static constexpr decltype(auto)
-      __do_visit_invoke(_Visitor&& __visitor, _Variants... __vars)
-      {
-	return __visit_invoke_impl(std::forward<_Visitor>(__visitor),
-				   std::forward<_Variants>(__vars)...);
-      }
-
-      // Perform the implicit conversion to _Result_type for std::visit<R>.
-      static constexpr _Result_type
-      __do_visit_invoke_r(_Visitor&& __visitor, _Variants... __vars)
-      {
-	return __visit_invoke_impl(std::forward<_Visitor>(__visitor),
-				   std::forward<_Variants>(__vars)...);
-      }
-
-      static constexpr decltype(auto)
-      __visit_invoke(_Visitor&& __visitor, _Variants... __vars)
-      {
-	if constexpr (__same_return_types)
-	  return __do_visit_invoke(std::forward<_Visitor>(__visitor),
-				   std::forward<_Variants>(__vars)...);
-	else
-	  return __do_visit_invoke_r(std::forward<_Visitor>(__visitor),
-				     std::forward<_Variants>(__vars)...);
+	else // for std::visit<R> use INVOKE<R>
+	  return std::__invoke_r<_Result_type>(
+	      std::forward<_Visitor>(__visitor),
+	      __variant::__get<__indices>(std::forward<_Variants>(__vars))...);
       }
 
       static constexpr auto
@@ -1041,8 +1048,7 @@ namespace __variant
       { return _Array_type{&__visit_invoke}; }
     };
 
-  template<bool __same_return_types,
-	   typename _Result_type, typename _Visitor, typename... _Variants>
+  template<typename _Result_type, typename _Visitor, typename... _Variants>
     struct __gen_vtable
     {
       using _Array_type =
@@ -1050,9 +1056,7 @@ namespace __variant
 		       variant_size_v<remove_reference_t<_Variants>>...>;
 
       static constexpr _Array_type _S_vtable
-	= __gen_vtable_impl<__same_return_types,
-			    _Array_type, tuple<_Variants...>,
-			    std::index_sequence<>>::_S_apply();
+	= __gen_vtable_impl<_Array_type, std::index_sequence<>>::_S_apply();
     };
 
   template<size_t _Np, typename _Tp>
@@ -1184,10 +1188,8 @@ namespace __variant
 				 const variant<_Types...>& __rhs) \
     { \
       bool __ret = true; \
-      __do_visit<__detail::__variant::__visit_with_index>( \
-        [&__ret, &__lhs] \
-		 (auto&& __rhs_mem, auto __rhs_index) mutable \
-		   -> __detail::__variant::__variant_idx_cookie \
+      __detail::__variant::__raw_idx_visit( \
+        [&__ret, &__lhs] (auto&& __rhs_mem, auto __rhs_index) mutable \
         { \
 	  if constexpr (__rhs_index != variant_npos) \
 	    { \
@@ -1201,7 +1203,6 @@ namespace __variant
             } \
           else \
             __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
-	  return {}; \
 	}, __rhs); \
       return __ret; \
     } \
@@ -1521,10 +1522,8 @@ namespace __variant
       noexcept((__is_nothrow_swappable<_Types>::value && ...)
 	       && is_nothrow_move_constructible_v<variant>)
       {
-	__do_visit<__detail::__variant::__visit_with_index>(
-	  [this, &__rhs](auto&& __rhs_mem,
-			 auto __rhs_index) mutable
-	    -> __detail::__variant::__variant_idx_cookie
+	__detail::__variant::__raw_idx_visit(
+	  [this, &__rhs](auto&& __rhs_mem, auto __rhs_index) mutable
 	  {
 	    if constexpr (__rhs_index != variant_npos)
 	      {
@@ -1560,7 +1559,6 @@ namespace __variant
 		    this->_M_reset();
 		  }
 	      }
-	    return {};
 	  }, __rhs);
       }
 
@@ -1638,21 +1636,11 @@ namespace __variant
       return __detail::__variant::__get<_Np>(std::move(__v));
     }
 
-  template<bool __use_index,
-	   bool __same_return_types,
-	   typename _Visitor, typename... _Variants>
+  template<typename _Result_type, typename _Visitor, typename... _Variants>
     constexpr decltype(auto)
     __do_visit(_Visitor&& __visitor, _Variants&&... __variants)
     {
-      using _Deduced_type = std::invoke_result<_Visitor,
-	decltype(std::get<0>(std::declval<_Variants>()))...>;
-
-      using _Result_type = typename std::conditional_t<__use_index,
-	__detail::__variant::__variant_idx_cookie,
-	_Deduced_type>::type;
-
       constexpr auto& __vtable = __detail::__variant::__gen_vtable<
-	__same_return_types,
 	_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
 
       auto __func_ptr = __vtable._M_access(__variants.index()...);
@@ -1667,8 +1655,13 @@ namespace __variant
       if ((__variants.valueless_by_exception() || ...))
 	__throw_bad_variant_access("Unexpected index");
 
-      return __do_visit(std::forward<_Visitor>(__visitor),
-			std::forward<_Variants>(__variants)...);
+      using _Result_type = std::invoke_result_t<_Visitor,
+	decltype(std::get<0>(std::declval<_Variants>()))...>;
+
+      using _Tag = __detail::__variant::__deduce_visit_result<_Result_type>;
+
+      return __do_visit<_Tag>(std::forward<_Visitor>(__visitor),
+			      std::forward<_Variants>(__variants)...);
     }
 
 #if __cplusplus > 201703L
@@ -1679,12 +1672,8 @@ namespace __variant
       if ((__variants.valueless_by_exception() || ...))
 	__throw_bad_variant_access("Unexpected index");
 
-      if constexpr (std::is_void_v<_Res>)
-        (void) __do_visit<false, false>(std::forward<_Visitor>(__visitor),
-					std::forward<_Variants>(__variants)...);
-      else
-	return __do_visit<false, false>(std::forward<_Visitor>(__visitor),
-					std::forward<_Variants>(__variants)...);
+      return __do_visit<_Res>(std::forward<_Visitor>(__visitor),
+			      std::forward<_Variants>(__variants)...);
     }
 #endif
 
@@ -1696,8 +1685,8 @@ namespace __variant
       noexcept((is_nothrow_invocable_v<hash<decay_t<_Types>>, _Types> && ...))
       {
 	size_t __ret;
-	__do_visit([&__t, &__ret](auto&& __t_mem) mutable
-		   -> __detail::__variant::__variant_cookie
+	__detail::__variant::__raw_visit(
+	  [&__t, &__ret](auto&& __t_mem) mutable
 	  {
 	    using _Type = __remove_cvref_t<decltype(__t_mem)>;
 	    if constexpr (!is_same_v<_Type,
@@ -1706,7 +1695,6 @@ namespace __variant
 		      + std::hash<_Type>{}(__t_mem);
 	    else
 	      __ret = std::hash<size_t>{}(__t.index());
-	    return {};
 	  }, __t);
 	return __ret;
       }

Reply via email to