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.

_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