On Tue, 26 Aug 2025, Tomasz Kaminski wrote:

> 
> 
> On Tue, Aug 26, 2025 at 5:50 PM Patrick Palka <ppa...@redhat.com> wrote:
>       On Tue, 26 Aug 2025, Jonathan Wakely wrote:
> 
>       > On 20/08/25 10:13 +0200, Tomasz Kamiński wrote:
>       > > This patch refactors the implementation of bind_front and bind_back 
> to
>       > > avoid using std::tuple for argument storage. Instead, bound 
> arguments are
>       > > now:
>       > > * stored directly if there is only one,
>       > > * within a dedicated _Bound_arg_storage otehrwise.
>       >
>       > "otehrwise" -> "otherwise"
>       >
>       > As Patrick requested, I think it would be useful to mention (briefly)
>       > that _Bound_arg_storage is cheaper to instantiate and access than
>       > std::tuple, and that it can be trivially copyable because it doesn't
>       > need a non-trivial assignment operator to deal with references types.
>       >
>       > OK for trunk with those changes.
>       >
>       > I agree with adding _Partial to the list of types to audit before
>       > calling C++20 stable.
> 
>       I just remembered that _Partial already has a partial specialization
>       for the single-bound-arg case that is optimal.  And the vast majority of
>       current standard range adaptors (or perhaps all) take at most one bound
>       arg so we wouldn't see much real-world benefit optimizing the _Partial
>       primary template, but I suppose we should still do it.
> 
> I wonder if we should make _Bind_back_t available in C++20 (without the 
> bind_back
> funciton), and just derive _Patial from it. And remove partial 
> specializations.
> 

We could do that for the primary template, but _Partial also has partial
specializations for "simple" bound arguments (e.g. views::take(5)) where
perfect forwarding isn't observable and so we don't need multiple
operator() overloads in C++20 mode.  This is a nice compile-time
optimization and diagnostic improvement that we probably should keep
around.


> We may need to do dance around explicit this, but I think it may be 
> worthwhile exploring.
> 
>       >
>       > > _Bound_arg_storage holds each argument in an _Indexed_bound_arg 
> base object.
>       > > The base class is parameterized by both type and index to allow 
> storing
>       > > multiple arguments of the same type. Invocations are handled by
>       > > _S_apply_front
>       > > amd _S_apply_back static functions, which simulate explicit object
>       > > parameters.
>       > > To facilitate this, the __like_t alias template is now 
> unconditionally
>       > > available
>       > > since C++11 in bits/move.h.
>       > >
>       > > libstdc++-v3/ChangeLog:
>       > >
>       > >     * include/bits/move.h (std::__like_impl, std::__like_t): Make
>       > >     available in c++11.
>       > >     * include/std/functional (std::_Indexed_bound_arg)
>       > >     (std::_Bound_arg_storage, std::__make_bound_args): Define.
>       > >     (std::_Bind_front, std::_Bind_back): Use _Bound_arg_storage.
>       > >     * testsuite/20_util/function_objects/bind_back/1.cc: Expand
>       > >     test to cover cases of 0, 1, many bound args.
>       > >     * testsuite/20_util/function_objects/bind_back/111327.cc: 
> Likewise.
>       > >     * testsuite/20_util/function_objects/bind_front/1.cc: Likewise.
>       > >     * testsuite/20_util/function_objects/bind_front/111327.cc: 
> Likewise.
>       > >
>       > > Signed-off-by: Tomasz Kamiński <tkami...@redhat.com>
>       > > ---
>       > > Changes in v2:
>       > > * replace few 8 spaces with tabs
>       > > * add more test cases, covering materialization
>       > >
>       > > Testing on x86_64-linux, all bind test passed.
>       > > OK for trunk when test passes.
>       > >
>       > > libstdc++-v3/include/bits/move.h              |   2 +-
>       > > libstdc++-v3/include/std/functional           | 113 +++++++++---
>       > > .../20_util/function_objects/bind_back/1.cc   | 166 
> ++++++++++++++++--
>       > > .../function_objects/bind_back/111327.cc      |  11 ++
>       > > .../20_util/function_objects/bind_front/1.cc  | 164 
> +++++++++++++++--
>       > > .../function_objects/bind_front/111327.cc     |  11 ++
>       > > 6 files changed, 418 insertions(+), 49 deletions(-)
>       > >
>       > > diff --git a/libstdc++-v3/include/bits/move.h
>       > > b/libstdc++-v3/include/bits/move.h
>       > > index 061e6b4de3d..8c4f461a110 100644
>       > > --- a/libstdc++-v3/include/bits/move.h
>       > > +++ b/libstdc++-v3/include/bits/move.h
>       > > @@ -89,7 +89,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >       return static_cast<_Tp&&>(__t);
>       > >     }
>       > >
>       > > -#if __glibcxx_forward_like // C++ >= 23
>       > >   template<typename _Tp, typename _Up>
>       > >   struct __like_impl; // _Tp must be a reference and _Up an lvalue 
> reference
>       > >
>       > > @@ -112,6 +111,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >   template<typename _Tp, typename _Up>
>       > >     using __like_t = typename __like_impl<_Tp&&, _Up&>::type;
>       > >
>       > > +#if __glibcxx_forward_like // C++ >= 23
>       > >   /** @brief Forward with the cv-qualifiers and value category of 
> another
>       > > type.
>       > >    *  @tparam _Tp An lvalue reference or rvalue reference.
>       > >    *  @tparam _Up An lvalue reference type deduced from the function
>       > > argument.
>       > > diff --git a/libstdc++-v3/include/std/functional
>       > > b/libstdc++-v3/include/std/functional
>       > > index 307bcb95bcc..b1cda87929d 100644
>       > > --- a/libstdc++-v3/include/std/functional
>       > > +++ b/libstdc++-v3/include/std/functional
>       > > @@ -922,6 +922,53 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     }
>       > >
>       > > #ifdef __cpp_lib_bind_front // C++ >= 20
>       > > +  template<size_t, typename _Tp>
>       > > +  struct _Indexed_bound_arg
>       > > +    {
>       > > +      [[no_unique_address]] _Tp _M_val;
>       > > +    };
>       > > +
>       > > +  template<typename... _IndexedArgs>
>       > > +    struct _Bound_arg_storage : _IndexedArgs...
>       > > +    {
>       > > +      template<typename _Fd, typename _Self, typename... _CallArgs>
>       > > +   static constexpr
>       > > +   decltype(auto)
>       > > +   _S_apply_front(_Fd&& __fd, _Self&& __self, _CallArgs&&... 
> __call_args)
>       > > +   {
>       > > +     return std::invoke(std::forward<_Fd>(__fd),
>       > > +                        __like_t<_Self, 
> _IndexedArgs>(__self)._M_val...,
>       > > +                        std::forward<_CallArgs>(__call_args)...);
>       > > +   }
>       > > +
>       > > +      template<typename _Fd, typename _Self, typename... _CallArgs>
>       > > +   static constexpr
>       > > +   decltype(auto)
>       > > +   _S_apply_back(_Fd&& __fd, _Self&& __self, _CallArgs&&... 
> __call_args)
>       > > +   {
>       > > +     return std::invoke(std::forward<_Fd>(__fd),
>       > > +                        std::forward<_CallArgs>(__call_args)...,
>       > > +                        __like_t<_Self, 
> _IndexedArgs>(__self)._M_val...);
>       > > +   }
>       > > +    };
>       > > +
>       > > +  template<typename... _BoundArgs, typename... _Args>
>       > > +    constexpr auto
>       > > +    __make_bound_args(_Args&&... __args)
>       > > +    {
>       > > +      if constexpr (sizeof...(_BoundArgs) == 1)
>       > > +   // pack has one element, so return copy of arg
>       > > +   return (_BoundArgs(std::forward<_Args>(__args)), ...);
>       > > +      else
>       > > +   {
>       > > +     auto __impl = [&]<size_t... _Inds>(index_sequence<_Inds...>)
>       > > +     {
>       > > +       return _Bound_arg_storage<_Indexed_bound_arg<_Inds,
>       > > _BoundArgs>...>
>       > > +              { {_BoundArgs(std::forward<_Args>(__args))}... };
>       > > +     };
>       > > +     return __impl(index_sequence_for<_BoundArgs...>());
>       > > +   }
>       > > +    }
>       > >
>       > >   template<typename _Fd, typename... _BoundArgs>
>       > >     struct _Bind_front
>       > > @@ -937,7 +984,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     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)...)
>       > > +
>       > > 
> _M_bound_args(__make_bound_args<_BoundArgs...>(std::forward<_Args>(__args)...))
>       > >     { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); }
>       > >
>       > > #if __cpp_explicit_this_parameter
>       > > @@ -948,7 +995,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     noexcept(is_nothrow_invocable_v<__like_t<_Self, _Fd>,
>       > >                                     __like_t<_Self, _BoundArgs>...,
>       > > _CallArgs...>)
>       > >     {
>       > > -     return _S_call(__like_t<_Self, _Bind_front>(__self),
>       > > _BoundIndices(),
>       > > +     return _S_call(__like_t<_Self, _Bind_front>(__self),
>       > >                      std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > > #else
>       > > @@ -959,8 +1006,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     operator()(_CallArgs&&... __call_args) &
>       > >     noexcept(is_nothrow_invocable_v<_Fd&, _BoundArgs&..., 
> _CallArgs...>)
>       > >     {
>       > > -     return _S_call(*this, _BoundIndices(),
>       > > -         std::forward<_CallArgs>(__call_args)...);
>       > > +     return _S_call(*this, 
> std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >       template<typename... _CallArgs>
>       > > @@ -971,8 +1017,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     noexcept(is_nothrow_invocable_v<const _Fd&, const 
> _BoundArgs&...,
>       > >                                     _CallArgs...>)
>       > >     {
>       > > -     return _S_call(*this, _BoundIndices(),
>       > > -         std::forward<_CallArgs>(__call_args)...);
>       > > +     return _S_call(*this, 
> std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >       template<typename... _CallArgs>
>       > > @@ -982,8 +1027,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     operator()(_CallArgs&&... __call_args) &&
>       > >     noexcept(is_nothrow_invocable_v<_Fd, _BoundArgs..., 
> _CallArgs...>)
>       > >     {
>       > > -     return _S_call(std::move(*this), _BoundIndices(),
>       > > -         std::forward<_CallArgs>(__call_args)...);
>       > > +     return _S_call(std::move(*this),
>       > > +                    std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >       template<typename... _CallArgs>
>       > > @@ -994,8 +1039,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     noexcept(is_nothrow_invocable_v<const _Fd, const _BoundArgs...,
>       > >                                     _CallArgs...>)
>       > >     {
>       > > -     return _S_call(std::move(*this), _BoundIndices(),
>       > > -         std::forward<_CallArgs>(__call_args)...);
>       > > +     return _S_call(std::move(*this),
>       > > +                    std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >       template<typename... _CallArgs>
>       > > @@ -1012,20 +1057,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > > #endif
>       > >
>       > >     private:
>       > > -      using _BoundIndices = index_sequence_for<_BoundArgs...>;
>       > > +      using _BoundArgsStorage
>       > > +   // _BoundArgs are required to be move-constructible, so this is 
> valid.
>       > > +   =
>       > > 
> decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...));
>       > >
>       > > -      template<typename _Tp, size_t... _Ind, typename... _CallArgs>
>       > > +      template<typename _Tp, typename... _CallArgs>
>       > >     static constexpr
>       > >     decltype(auto)
>       > > -   _S_call(_Tp&& __g, index_sequence<_Ind...>, _CallArgs&&...
>       > > __call_args)
>       > > +   _S_call(_Tp&& __g, _CallArgs&&... __call_args)
>       > >     {
>       > > -     return std::invoke(std::forward<_Tp>(__g)._M_fd,
>       > > -         std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)...,
>       > > -         std::forward<_CallArgs>(__call_args)...);
>       > > +     if constexpr (sizeof...(_BoundArgs) == 1)
>       > > +       return std::invoke(std::forward<_Tp>(__g)._M_fd,
>       > > +                          std::forward<_Tp>(__g)._M_bound_args,
>       > > +                          std::forward<_CallArgs>(__call_args)...);
>       > > +     else
>       > > +       return _BoundArgsStorage::_S_apply_front(
>       > > +                 std::forward<_Tp>(__g)._M_fd,
>       > > +                 std::forward<_Tp>(__g)._M_bound_args,
>       > > +                 std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >       [[no_unique_address]] _Fd _M_fd;
>       > > -      [[no_unique_address]] std::tuple<_BoundArgs...> 
> _M_bound_args;
>       > > +      [[no_unique_address]] _BoundArgsStorage _M_bound_args;
>       > >     };
>       > >
>       > >   template<typename _Fn, typename... _Args>
>       > > @@ -1066,7 +1119,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     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)...)
>       > > +
>       > > 
> _M_bound_args(__make_bound_args<_BoundArgs...>(std::forward<_Args>(__args)...))
>       > >     { static_assert(sizeof...(_Args) == sizeof...(_BoundArgs)); }
>       > >
>       > >       template<typename _Self, typename... _CallArgs>
>       > > @@ -1076,25 +1129,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       > >     noexcept(is_nothrow_invocable_v<__like_t<_Self, _Fd>,
>       > >                                     _CallArgs..., __like_t<_Self,
>       > > _BoundArgs>...>)
>       > >     {
>       > > -     return _S_call(__like_t<_Self, _Bind_back>(__self), 
> _BoundIndices(),
>       > > +     return _S_call(__like_t<_Self, _Bind_back>(__self),
>       > >                      std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >     private:
>       > > -      using _BoundIndices = index_sequence_for<_BoundArgs...>;
>       > > +      using _BoundArgsStorage
>       > > +   // _BoundArgs are required to be move-constructible, so this is 
> valid.
>       > > +   =
>       > > 
> decltype(__make_bound_args<_BoundArgs...>(std::declval<_BoundArgs>()...));
>       > >
>       > > -      template<typename _Tp, size_t... _Ind, typename... _CallArgs>
>       > > +      template<typename _Tp, typename... _CallArgs>
>       > >     static constexpr
>       > >     decltype(auto)
>       > > -   _S_call(_Tp&& __g, index_sequence<_Ind...>, _CallArgs&&...
>       > > __call_args)
>       > > +   _S_call(_Tp&& __g, _CallArgs&&... __call_args)
>       > >     {
>       > > -     return std::invoke(std::forward<_Tp>(__g)._M_fd,
>       > > -         std::forward<_CallArgs>(__call_args)...,
>       > > -         std::get<_Ind>(std::forward<_Tp>(__g)._M_bound_args)...);
>       > > +     if constexpr (sizeof...(_BoundArgs) == 1)
>       > > +       return std::invoke(std::forward<_Tp>(__g)._M_fd,
>       > > +                          std::forward<_CallArgs>(__call_args)...,
>       > > +                          std::forward<_Tp>(__g)._M_bound_args);
>       > > +     else
>       > > +       return _BoundArgsStorage::_S_apply_back(
>       > > +                 std::forward<_Tp>(__g)._M_fd,
>       > > +                 std::forward<_Tp>(__g)._M_bound_args,
>       > > +                 std::forward<_CallArgs>(__call_args)...);
>       > >     }
>       > >
>       > >       [[no_unique_address]] _Fd _M_fd;
>       > > -      [[no_unique_address]] std::tuple<_BoundArgs...> 
> _M_bound_args;
>       > > +      [[no_unique_address]] _BoundArgsStorage _M_bound_args;
>       > >     };
>       > >
>       > >   template<typename _Fn, typename... _Args>
>       > > diff --git 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
>       > > b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
>       > > index c31d3228815..a31528fc755 100644
>       > > --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
>       > > +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc
>       > > @@ -48,6 +48,16 @@ test01()
>       > >       decltype(bind_back(std::declval<const F&>(), 
> std::declval<const
>       > > int&>()))
>       > >       >);
>       > >
>       > > +  static_assert(std::is_same_v<
>       > > +      decltype(bind_back(std::declval<F>(), std::declval<int>(),
>       > > std::declval<float>())),
>       > > +      decltype(bind_back(std::declval<F&>(), std::declval<int&>(),
>       > > std::declval<float&>()))
>       > > +      >);
>       > > +  static_assert(std::is_same_v<
>       > > +      decltype(bind_back(std::declval<F>(), std::declval<int>(),
>       > > std::declval<float>())),
>       > > +      decltype(bind_back(std::declval<const F&>(), 
> std::declval<const
>       > > int&>(), std::declval<const float&>()))
>       > > +      >);
>       > > +
>       > > +
>       > >   // Reference wrappers should be handled:
>       > >   static_assert(!std::is_same_v<
>       > >       decltype(bind_back(std::declval<F>(), std::declval<int&>())),
>       > > @@ -63,29 +73,58 @@ test01()
>       > >       >);
>       > > }
>       > >
>       > > +struct quals
>       > > +{
>       > > +  bool as_const;
>       > > +  bool as_lvalue;
>       > > +};
>       > > +
>       > > +template<typename... Args>
>       > > void
>       > > -test02()
>       > > +testTarget(Args... args)
>       > > {
>       > > -  struct quals
>       > > +  struct F
>       > >   {
>       > > -    bool as_const;
>       > > -    bool as_lvalue;
>       > > +    quals operator()(Args...) & { return { false, true }; }
>       > > +    quals operator()(Args...) const & { return { true, true }; }
>       > > +    quals operator()(Args...) && { return { false, false }; }
>       > > +    quals operator()(Args...) const && { return { true, false }; }
>       > >   };
>       > >
>       > > +  F f;
>       > > +  auto g = bind_back(f, args...);
>       > > +  const auto& cg = g;
>       > > +  quals q;
>       > > +
>       > > +  // constness and value category should be forwarded to the 
> target object:
>       > > +  q = g();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = std::move(g)();
>       > > +  VERIFY( ! q.as_const && ! q.as_lvalue );
>       > > +  q = cg();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = std::move(cg)();
>       > > +  VERIFY( q.as_const && ! q.as_lvalue );
>       > > +}
>       > > +
>       > > +template<typename... Args>
>       > > +void
>       > > +testBoundArgs(Args... args)
>       > > +{
>       > >   struct F
>       > >   {
>       > > -    quals operator()() & { return { false, true }; }
>       > > -    quals operator()() const & { return { true, true }; }
>       > > -    quals operator()() && { return { false, false }; }
>       > > -    quals operator()() const && { return { true, false }; }
>       > > +    quals operator()(Args..., int&) const { return { false, true 
> }; }
>       > > +    quals operator()(Args..., int const&) const { return { true, 
> true }; }
>       > > +    quals operator()(Args..., int&&) const { return { false, false 
> }; }
>       > > +    quals operator()(Args..., int const&&) const { return { true, 
> false };
>       > > }
>       > >   };
>       > >
>       > >   F f;
>       > > -  auto g = bind_back(f);
>       > > +  auto g = bind_back(f, args..., 10);
>       > >   const auto& cg = g;
>       > >   quals q;
>       > >
>       > > -  // constness and value category should be forwarded to the 
> target object:
>       > > +  // constness and value category should be forwarded to the bound 
> objects:
>       > >   q = g();
>       > >   VERIFY( ! q.as_const && q.as_lvalue );
>       > >   q = std::move(g)();
>       > > @@ -94,6 +133,70 @@ test02()
>       > >   VERIFY( q.as_const && q.as_lvalue );
>       > >   q = std::move(cg)();
>       > >   VERIFY( q.as_const && ! q.as_lvalue );
>       > > +
>       > > +  int i = 0;
>       > > +  auto gr = bind_back(f, args..., std::ref(i));
>       > > +  const auto& cgr = gr;
>       > > +
>       > > +  // bound object is reference wrapper
>       > > +  q = gr();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = std::move(gr)();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = cgr();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = std::move(cgr)();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +
>       > > +  auto gcr = bind_back(f, args..., std::cref(i));
>       > > +  const auto& cgcr = gcr;
>       > > +
>       > > +  q = gcr();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = std::move(gcr)();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = cgcr();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = std::move(cgcr)();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +}
>       > > +
>       > > +template<typename... Args>
>       > > +void
>       > > +testCallArgs(Args... args)
>       > > +{
>       > > +  struct F
>       > > +  {
>       > > +    quals operator()(int&, Args...) const { return { false, true 
> }; }
>       > > +    quals operator()(int const&, Args...) const { return { true, 
> true }; }
>       > > +    quals operator()(int&&, Args...) const { return { false, false 
> }; }
>       > > +    quals operator()(int const&&, Args...) const { return { true, 
> false };
>       > > }
>       > > +  };
>       > > +
>       > > +  F f;
>       > > +  auto g = bind_back(f, args...);
>       > > +  const auto& cg = g;
>       > > +  quals q;
>       > > +  int i = 10;
>       > > +  const int ci = i;
>       > > +
>       > > +  q = g(i);
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = g(std::move(i));
>       > > +  VERIFY( ! q.as_const && ! q.as_lvalue );
>       > > +  q = g(ci);
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = g(std::move(ci));
>       > > +  VERIFY( q.as_const && ! q.as_lvalue );
>       > > +
>       > > +  q = cg(i);
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = cg(std::move(i));
>       > > +  VERIFY( ! q.as_const && ! q.as_lvalue );
>       > > +  q = cg(ci);
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = cg(std::move(ci));
>       > > +  VERIFY( q.as_const && ! q.as_lvalue );
>       > > }
>       > >
>       > > void
>       > > @@ -168,11 +271,52 @@ test04()
>       > >   return true;
>       > > }
>       > >
>       > > +struct CountedArg
>       > > +{
>       > > +  CountedArg() = default;
>       > > +  CountedArg(CountedArg&& f) noexcept : counter(f.counter) { 
> ++counter; }
>       > > +  CountedArg& operator=(CountedArg&&) = delete;
>       > > +
>       > > +  int counter = 0;
>       > > +};
>       > > +CountedArg const c;
>       > > +
>       > > +void
>       > > +testMaterialization()
>       > > +{
>       > > +  struct F
>       > > +  {
>       > > +    int operator()(CountedArg arg, int) const
>       > > +    { return arg.counter; };
>       > > +  };
>       > > +
>       > > +  // CountedArg is bound to rvalue-reference thus moved
>       > > +  auto f0 = std::bind_back(F{});
>       > > +  VERIFY( f0(CountedArg(), 10) == 1 );
>       > > +
>       > > +  auto f1 = std::bind_back(F{}, 10);
>       > > +  VERIFY( f1(CountedArg()) == 1 );
>       > > +}
>       > > +
>       > > int
>       > > main()
>       > > {
>       > >   test01();
>       > > -  test02();
>       > >   test03();
>       > > +
>       > > +  testTarget();
>       > > +  testTarget(10);
>       > > +  testTarget(10, 20, 30);
>       > > +
>       > > +  testBoundArgs();
>       > > +  testBoundArgs(10);
>       > > +  testBoundArgs(10, 20, 30);
>       > > +
>       > > +  testCallArgs();
>       > > +  testCallArgs(10);
>       > > +  testCallArgs(10, 20, 30);
>       > > +
>       > > +  testMaterialization();
>       > > +
>       > >   static_assert(test04());
>       > > }
>       > > diff --git
>       > > 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc
>       > > 
> b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc
>       > > index d634db9dc1d..de3ae47e37f 100644
>       > > --- 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc
>       > > +++ 
> b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc
>       > > @@ -37,6 +37,17 @@ int main() {
>       > >   g1(); // { dg-error "deleted|no match" }
>       > >   std::move(g1)(); // { dg-error "deleted|no match" }
>       > >   std::move(std::as_const(g1))();
>       > > +
>       > > +  auto f2 = std::bind_back(F{}, 42, 10);
>       > > +  f2(); // { dg-error "deleted|no match" }
>       > > +  std::move(f2)();
>       > > +  std::as_const(f2)();
>       > > +  std::move(std::as_const(f2))();
>       > > +
>       > > +  auto g2 = std::bind_back(G{}, 42, 10);
>       > > +  g2(); // { dg-error "deleted|no match" }
>       > > +  std::move(g2)(); // { dg-error "deleted|no match" }
>       > > +  std::move(std::as_const(g2))();
>       > > }
>       > >
>       > > // { dg-error "no type named 'type' in 'struct std::invoke_result" 
> "" {
>       > > target c++23 } 0 }
>       > > diff --git 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
>       > > b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
>       > > index 57482c52263..ef28de8321b 100644
>       > > --- 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
>       > > +++ 
> b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc
>       > > @@ -48,6 +48,15 @@ test01()
>       > >       decltype(bind_front(std::declval<const F&>(), 
> std::declval<const
>       > > int&>()))
>       > >       >);
>       > >
>       > > +  static_assert(std::is_same_v<
>       > > +      decltype(bind_front(std::declval<F>(), std::declval<int>(),
>       > > std::declval<float>())),
>       > > +      decltype(bind_front(std::declval<F&>(), std::declval<int&>(),
>       > > std::declval<float&>()))
>       > > +      >);
>       > > +  static_assert(std::is_same_v<
>       > > +      decltype(bind_front(std::declval<F>(), std::declval<int>(),
>       > > std::declval<float>())),
>       > > +      decltype(bind_front(std::declval<const F&>(), 
> std::declval<const
>       > > int&>(), std::declval<const float&>()))
>       > > +      >);
>       > > +
>       > >   // Reference wrappers should be handled:
>       > >   static_assert(!std::is_same_v<
>       > >       decltype(bind_front(std::declval<F>(), std::declval<int&>())),
>       > > @@ -63,29 +72,58 @@ test01()
>       > >       >);
>       > > }
>       > >
>       > > +struct quals
>       > > +{
>       > > +  bool as_const;
>       > > +  bool as_lvalue;
>       > > +};
>       > > +
>       > > +template<typename... Args>
>       > > void
>       > > -test02()
>       > > +testTarget(Args... args)
>       > > {
>       > > -  struct quals
>       > > +  struct F
>       > >   {
>       > > -    bool as_const;
>       > > -    bool as_lvalue;
>       > > +    quals operator()(Args...) & { return { false, true }; }
>       > > +    quals operator()(Args...) const & { return { true, true }; }
>       > > +    quals operator()(Args...) && { return { false, false }; }
>       > > +    quals operator()(Args...) const && { return { true, false }; }
>       > >   };
>       > >
>       > > +  F f;
>       > > +  auto g = bind_front(f, args...);
>       > > +  const auto& cg = g;
>       > > +  quals q;
>       > > +
>       > > +  // constness and value category should be forwarded to the 
> target object:
>       > > +  q = g();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = std::move(g)();
>       > > +  VERIFY( ! q.as_const && ! q.as_lvalue );
>       > > +  q = cg();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = std::move(cg)();
>       > > +  VERIFY( q.as_const && ! q.as_lvalue );
>       > > +}
>       > > +
>       > > +template<typename... Args>
>       > > +void
>       > > +testBoundArgs(Args... args)
>       > > +{
>       > >   struct F
>       > >   {
>       > > -    quals operator()() & { return { false, true }; }
>       > > -    quals operator()() const & { return { true, true }; }
>       > > -    quals operator()() && { return { false, false }; }
>       > > -    quals operator()() const && { return { true, false }; }
>       > > +    quals operator()(Args..., int&) const { return { false, true 
> }; }
>       > > +    quals operator()(Args..., int const&) const { return { true, 
> true }; }
>       > > +    quals operator()(Args..., int&&) const { return { false, false 
> }; }
>       > > +    quals operator()(Args..., int const&&) const { return { true, 
> false };
>       > > }
>       > >   };
>       > >
>       > >   F f;
>       > > -  auto g = bind_front(f);
>       > > +  auto g = bind_front(f, args..., 10);
>       > >   const auto& cg = g;
>       > >   quals q;
>       > >
>       > > -  // constness and value category should be forwarded to the 
> target object:
>       > > +  // constness and value category should be forwarded to the bound 
> objects:
>       > >   q = g();
>       > >   VERIFY( ! q.as_const && q.as_lvalue );
>       > >   q = std::move(g)();
>       > > @@ -94,6 +132,70 @@ test02()
>       > >   VERIFY( q.as_const && q.as_lvalue );
>       > >   q = std::move(cg)();
>       > >   VERIFY( q.as_const && ! q.as_lvalue );
>       > > +
>       > > +  int i = 0;
>       > > +  auto gr = bind_front(f, args..., std::ref(i));
>       > > +  const auto& cgr = gr;
>       > > +
>       > > +  // bound object is reference wrapper, converts to same type of 
> reference
>       > > +  q = gr();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = std::move(gr)();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = cgr();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = std::move(cgr)();
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +
>       > > +  auto gcr = bind_front(f, args..., std::cref(i));
>       > > +  const auto& cgcr = gcr;
>       > > +
>       > > +  q = gcr();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = std::move(gcr)();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = cgcr();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = std::move(cgcr)();
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +}
>       > > +
>       > > +template<typename... Args>
>       > > +void
>       > > +testCallArgs(Args... args)
>       > > +{
>       > > +  struct F
>       > > +  {
>       > > +    quals operator()(Args..., int&) const { return { false, true 
> }; }
>       > > +    quals operator()(Args..., int const&) const { return { true, 
> true }; }
>       > > +    quals operator()(Args..., int&&) const { return { false, false 
> }; }
>       > > +    quals operator()(Args..., int const&&) const { return { true, 
> false };
>       > > }
>       > > +  };
>       > > +
>       > > +  F f;
>       > > +  auto g = bind_front(f, args...);
>       > > +  const auto& cg = g;
>       > > +  quals q;
>       > > +  int i = 10;
>       > > +  const int ci = i;
>       > > +
>       > > +  q = g(i);
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = g(std::move(i));
>       > > +  VERIFY( ! q.as_const && ! q.as_lvalue );
>       > > +  q = g(ci);
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = g(std::move(ci));
>       > > +  VERIFY( q.as_const && ! q.as_lvalue );
>       > > +
>       > > +  q = cg(i);
>       > > +  VERIFY( ! q.as_const && q.as_lvalue );
>       > > +  q = cg(std::move(i));
>       > > +  VERIFY( ! q.as_const && ! q.as_lvalue );
>       > > +  q = cg(ci);
>       > > +  VERIFY( q.as_const && q.as_lvalue );
>       > > +  q = cg(std::move(ci));
>       > > +  VERIFY( q.as_const && ! q.as_lvalue );
>       > > }
>       > >
>       > > void
>       > > @@ -167,11 +269,51 @@ test04()
>       > >   VERIFY( bind_front(g2, 3)() == 6 );
>       > > }
>       > >
>       > > +struct CountedArg
>       > > +{
>       > > +  CountedArg() = default;
>       > > +  CountedArg(CountedArg&& f) noexcept : counter(f.counter) { 
> ++counter; }
>       > > +  CountedArg& operator=(CountedArg&&) = delete;
>       > > +
>       > > +  int counter = 0;
>       > > +};
>       > > +CountedArg const c;
>       > > +
>       > > +void
>       > > +testMaterialization()
>       > > +{
>       > > +  struct F
>       > > +  {
>       > > +    int operator()(int, CountedArg arg) const
>       > > +    { return arg.counter; };
>       > > +  };
>       > > +
>       > > +  // CountedArg is bound to rvalue-reference thus moved
>       > > +  auto f0 = std::bind_front(F{});
>       > > +  VERIFY( f0(10, CountedArg()) == 1 );
>       > > +
>       > > +  auto f1 = std::bind_front(F{}, 10);
>       > > +  VERIFY( f1(CountedArg()) == 1 );
>       > > +}
>       > > +
>       > > int
>       > > main()
>       > > {
>       > >   test01();
>       > > -  test02();
>       > >   test03();
>       > >   test04();
>       > > +
>       > > +  testTarget();
>       > > +  testTarget(10);
>       > > +  testTarget(10, 20, 30);
>       > > +
>       > > +  testBoundArgs();
>       > > +  testBoundArgs(10);
>       > > +  testBoundArgs(10, 20, 30);
>       > > +
>       > > +  testCallArgs();
>       > > +  testCallArgs(10);
>       > > +  testCallArgs(10, 20, 30);
>       > > +
>       > > +  testMaterialization();
>       > > }
>       > > diff --git
>       > > 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
>       > > 
> b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
>       > > index 5fe0a83baec..6694322d67e 100644
>       > > --- 
> a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
>       > > +++ 
> b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc
>       > > @@ -37,6 +37,17 @@ int main() {
>       > >   g1(); // { dg-error "deleted|no match" }
>       > >   std::move(g1)(); // { dg-error "deleted|no match" }
>       > >   std::move(std::as_const(g1))();
>       > > +
>       > > +  auto f2 = std::bind_front(F{}, 42, 10);
>       > > +  f2(); // { dg-error "deleted|no match" }
>       > > +  std::move(f2)();
>       > > +  std::as_const(f2)();
>       > > +  std::move(std::as_const(f2))();
>       > > +
>       > > +  auto g2 = std::bind_front(G{}, 42, 10);
>       > > +  g2(); // { dg-error "deleted|no match" }
>       > > +  std::move(g2)(); // { dg-error "deleted|no match" }
>       > > +  std::move(std::as_const(g2))();
>       > > }
>       > >
>       > > // { dg-error "no type named 'type' in 'struct std::invoke_result" 
> "" {
>       > > target c++23 } 0 }
>       > > --
>       > > 2.50.1
>       > >
>       > >
>       >
>       >
> 
> 
> 

Reply via email to