On 07/03/19 14:15 +0000, Jonathan Wakely wrote:
* include/std/functional [C++20] (_Bind_front, _Bind_front_t): Define helpers for bind_front. (bind_front, __cpp_lib_bind_front): Define. * testsuite/20_util/function_objects/bind_front/1.cc: New test.
I'm considering something like the attached patch (but with better names for __tag1 and __tag2 obviously). With this change bind_front would unwrap nested binders, so that bind_front(bind_front(f, 1), 2) would create a _Bind_front<F, int, int> instead of _Bind_front<_Bind_front<F, int>, int>. That would make the call go straight to the target object, instead of through an extra layer of wrapper (which should improve compile times, and also unoptimized runtime performance).
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 8cf2c670648..da61b00bd15 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -839,6 +839,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus > 201703L #define __cpp_lib_bind_front 201902L + struct __tag1; + struct __tag2; + template<typename _Fd, typename... _BoundArgs> struct _Bind_front { @@ -849,13 +852,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // instead of the copy/move constructor. template<typename _Fn, typename... _Args> explicit constexpr - _Bind_front(int, _Fn&& __fn, _Args&&... __args) + _Bind_front(__tag1*, _Fn&& __fn, _Args&&... __args) noexcept(__and_<is_nothrow_constructible<_Fd, _Fn>, is_nothrow_constructible<_BoundArgs, _Args>...>::value) : _M_fd(std::forward<_Fn>(__fn)), _M_bound_args(std::forward<_Args>(__args)...) { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); } + template<typename... _BArgs, typename... _Args> + explicit constexpr + _Bind_front(__tag2*, const _Bind_front<_Fd, _BArgs...>& __fn, + _Args&&... __args) + : _M_fd(__fn._M_fd), + _M_bound_args(std::tuple_cat(__fn._M_bound_args, + std::make_tuple(std::forward<_Args>(__args)...))) + { } + + template<typename... _BArgs, typename... _Args> + explicit constexpr + _Bind_front(__tag2*, _Bind_front<_Fd, _BArgs...>&& __fn, + _Args&&... __args) + : _M_fd(std::move(__fn._M_fd)), + _M_bound_args(std::tuple_cat(std::move(__fn._M_bound_args), + std::make_tuple(std::forward<_Args>(__args)...))) + { } + _Bind_front(const _Bind_front&) = default; _Bind_front(_Bind_front&&) = default; _Bind_front& operator=(const _Bind_front&) = default; @@ -919,19 +940,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Fd _M_fd; std::tuple<_BoundArgs...> _M_bound_args; + + template<typename _Fd2, typename... _BArgs> + friend class _Bind_front; }; template<typename _Fn, typename... _Args> - using _Bind_front_t - = _Bind_front<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>; + struct _Bind_front_helper + { + using type = _Bind_front<_Fn, _Args...>; + using __tag = __tag1*; + }; + + template<typename _Fn, typename... _BoundArgs, typename... _Args> + struct _Bind_front_helper<_Bind_front<_Fn, _BoundArgs...>, _Args...> + { + using type = _Bind_front<_Fn, _BoundArgs..., _Args...>; + using __tag = __tag2*; + }; + + template<typename _Fn, typename... _Args> + using _Bind_front_t = typename + _Bind_front_helper<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>::type; + + template<typename _Fn, typename... _Args> + using _Bind_front_tag = typename + _Bind_front_helper<decay_t<_Fn>, unwrap_ref_decay_t<_Args>...>::__tag; template<typename _Fn, typename... _Args> _Bind_front_t<_Fn, _Args...> bind_front(_Fn&& __fn, _Args&&... __args) - noexcept(is_nothrow_constructible_v<int, _Bind_front_t<_Fn, _Args...>, + noexcept(is_nothrow_constructible_v<_Bind_front_tag<_Fn, _Args...>, + _Bind_front_t<_Fn, _Args...>, _Fn, _Args...>) { - return _Bind_front_t<_Fn, _Args...>(0, std::forward<_Fn>(__fn), + return _Bind_front_t<_Fn, _Args...>(_Bind_front_tag<_Fn, _Args...>(), + std::forward<_Fn>(__fn), std::forward<_Args>(__args)...); } #endif