libstdc++: Use deducing this in std::bind when available [PR80564]

Message ID 20251205145558.1952982-1-ppalka@redhat.com
State New
Headers
Series libstdc++: Use deducing this in std::bind when available [PR80564] |

Commit Message

Patrick Palka Dec. 5, 2025, 2:55 p.m. UTC
  Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?

-- >8 --

Implement the cv-qual forwarding required by std::bind using deducing
this when available, instead of needing 4 operator() overloads.  Using
deducing this here is more complicated here than in other call
wrappers because std::bind is not really "perfect forwarding": it
doesn't forward value category, and along with const-ness it also
forwards volatile-ness (before C++20).

The old implementation suffers from the same problem that other
SFINAE-friendly call wrappers have which is solved by using deducing
this (see p5.5 of the deducing this paper P0847R7).

	PR libstdc++/80564

libstdc++-v3/ChangeLog:

	* include/std/functional (__cv_like): New.
	(_Bind::_Res_type): Don't define when not needed.
	(_Bind::__dependent): Likewise.
	(_Bind::_Res_type_cv): Likewise.
	(_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
	Define as two instead of four overloads using deducing
	this.
	* testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
	diagnostics inside headers.
	* testsuite/20_util/bind/ref_neg.cc: Likewise.
	* testsuite/20_util/bind/cv_quals_4.cc: New test.
---
 libstdc++-v3/include/std/functional           | 81 +++++++++++++++++++
 .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
 .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
 .../testsuite/20_util/bind/ref_neg.cc         |  1 +
 4 files changed, 124 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
  

Comments

Tomasz Kamiński Dec. 5, 2025, 3:06 p.m. UTC | #1
Just one small suggestion, otherwise LGTM.

On Fri, Dec 5, 2025 at 3:57 PM Patrick Palka <ppalka@redhat.com> wrote:

> Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?
>
> -- >8 --
>
> Implement the cv-qual forwarding required by std::bind using deducing
> this when available, instead of needing 4 operator() overloads.  Using
> deducing this here is more complicated here than in other call
> wrappers because std::bind is not really "perfect forwarding": it
> doesn't forward value category, and along with const-ness it also
> forwards volatile-ness (before C++20).
>
> The old implementation suffers from the same problem that other
> SFINAE-friendly call wrappers have which is solved by using deducing
> this (see p5.5 of the deducing this paper P0847R7).
>
>         PR libstdc++/80564
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/functional (__cv_like): New.
>         (_Bind::_Res_type): Don't define when not needed.
>         (_Bind::__dependent): Likewise.
>         (_Bind::_Res_type_cv): Likewise.
>         (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>         Define as two instead of four overloads using deducing
>         this.
>         * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>         diagnostics inside headers.
>         * testsuite/20_util/bind/ref_neg.cc: Likewise.
>         * testsuite/20_util/bind/cv_quals_4.cc: New test.
> ---
>  libstdc++-v3/include/std/functional           | 81 +++++++++++++++++++
>  .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>  .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
>  .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>  4 files changed, 124 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>
> diff --git a/libstdc++-v3/include/std/functional
> b/libstdc++-v3/include/std/functional
> index 1928a27d3fd6..9348356f03ce 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  # define _GLIBCXX_DEPR_BIND
>  #endif
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +  // Return a _Up that has the same cv-quals as _Tp.
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like
> +    { using type = _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<const _Tp, _Up>
> +    { using type = const _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<volatile _Tp, _Up>
> +    { using type = volatile _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<const volatile _Tp, _Up>
> +    { using type = const volatile _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
> +#endif
> +
>    /// Type of the function object returned from bind().
>    template<typename _Signature>
>      class _Bind;
> @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         using _Res_type_impl
>           = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
>
> +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>        template<typename _CallArgs>
>         using _Res_type = _Res_type_impl<_Functor, _CallArgs,
> _Bound_args...>;
>
> @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           typename __cv_quals<__dependent<_CallArgs>>::type,
>           _CallArgs,
>           typename __cv_quals<_Bound_args>::type...>;
> +#endif
>
>       public:
>        template<typename... _Args>
> @@ -593,6 +617,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _Bind(const _Bind&) = default;
>        _Bind(_Bind&&) = default;
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +# pragma GCC diagnostic push
> +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
> +      // Call unqualified
> +      template<typename... _Args,
> +              typename _Self,
> +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
> +              typename _Result
> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
> +                                 tuple<_Args...>,
> +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> +       _GLIBCXX20_CONSTEXPR
> +       _Result
> +       operator()(this _Self&& __self, _Args&&... __args)
> +       {
> +         if constexpr (is_const<_Self_nonref>::value)
> +           return static_cast<const _Bind&>(__self)
>
Once you have __cv_like_t, is there any reason to not use something like
here:
      using _Bind_ref_t = __cv_like_t<_Self_nonref, _Bind>&;
        _Bind_ref_t(__self).
I think we also need to use c-cast, if someone privately inherited from
bind specialization,
for some reason. I thinking of version of overload:
template<typename... Funcs>
class Overload : Funcs....
{
public:
     Overload(Funcs... func) : Funcs(funcs)... {}
     using Funcs::operator()....;
};

And then used with bind.

> +             .template __call_c<_Result>(std::forward_as_tuple
> +                                         (std::forward<_Args>(__args)...),
> +                                         _Bound_indexes());
> +         else
> +           return static_cast<_Bind&>(__self)
> +             .template __call<_Result>(std::forward_as_tuple
> +                                       (std::forward<_Args>(__args)...),
> +                                       _Bound_indexes());
> +       }
> +
> +# if defined(_GLIBCXX_VOLATILE_BIND)
> +      template<typename... _Args,
> +              typename _Self,
> +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> +              __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
> +              typename _Result
> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
> +                                 tuple<_Args...>,
> +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> +       _GLIBCXX_DEPR_BIND
> +       _Result
> +       operator()(this _Self&& __self, _Args&&... __args)
> +       {
> +         if constexpr (is_const<_Self_nonref>::value)
> +           return static_cast<const volatile _Bind&>(__self)
> +             .template __call_c_v<_Result>(std::forward_as_tuple
> +
>  (std::forward<_Args>(__args)...),
> +                                           _Bound_indexes());
> +         else
> +           return static_cast<volatile _Bind&>(__self)
> +             .template __call_v<_Result>(std::forward_as_tuple
> +                                         (std::forward<_Args>(__args)...),
> +                                         _Bound_indexes());
> +       }
> +# endif
> +# pragma GCC diagnostic pop
> +#else
>        // Call unqualified
>        template<typename... _Args,
>                typename _Result = _Res_type<tuple<_Args...>>>
> @@ -642,6 +722,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               _Bound_indexes());
>         }
>  #endif // volatile
> +#endif
>      };
>
>    /// Type of the function object returned from bind<R>().
> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> index e4c348f7a3ca..6d37cc43fd3a 100644
> --- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> @@ -44,6 +44,9 @@ void test01()
>                        // { dg-error "no match" "" { target c++20 } 43 }
>  }
>
> +// Ignore the reasons for deduction/substitution failure in the headers.
> +// { dg-prune-output "no type named 'type' in 'struct
> std::enable_if<false" }
> +
>  int main()
>  {
>    test01();
> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> new file mode 100644
> index 000000000000..365a6ff4e00f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> @@ -0,0 +1,39 @@
> +// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
> +// { dg-do compile { target c++14 } }
> +
> +#include <functional>
> +
> +struct A
> +{
> +  template<class T>
> +  auto operator()(T&)
> +  { }
> +
> +  template<class T>
> +  auto operator()(T&) const
> +  { T::fail; }
> +};
> +
> +void
> +test01()
> +{
> +  A a;
> +  std::bind(a, 0)(); // doesn't consider the const overload
> +  std::bind<void>(a, 0)();
> +}
> +
> +void
> +test02()
> +{
> +  auto f = [] (auto& x) { x = 1; };
> +  int i;
> +  std::bind(f, i)(); // doesn't try const-invoking the lambda
> +  std::bind<void>(f, i)();
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +  test02();
> +}
> diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> index dd47c437d426..46cc4bb330e2 100644
> --- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> @@ -51,6 +51,7 @@ void test02()
>  // Ignore the reasons for deduction/substitution failure in the headers.
>  // Arrange for the match to work on installed trees as well as build
> trees.
>  // { dg-prune-output "no type named 'type' in 'struct
> std::__invoke_result" }
> +// { dg-prune-output "no type named 'type' in 'struct
> std::enable_if<false" }
>
>  int main()
>  {
> --
> 2.52.0.154.gf0ef5b6d9b
>
>
  
Tomasz Kamiński Dec. 5, 2025, 3:07 p.m. UTC | #2
On Fri, Dec 5, 2025 at 4:06 PM Tomasz Kaminski <tkaminsk@redhat.com> wrote:

> Just one small suggestion, otherwise LGTM.
>
> On Fri, Dec 5, 2025 at 3:57 PM Patrick Palka <ppalka@redhat.com> wrote:
>
>> Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?
>>
>> -- >8 --
>>
>> Implement the cv-qual forwarding required by std::bind using deducing
>> this when available, instead of needing 4 operator() overloads.  Using
>> deducing this here is more complicated here than in other call
>> wrappers because std::bind is not really "perfect forwarding": it
>> doesn't forward value category, and along with const-ness it also
>> forwards volatile-ness (before C++20).
>>
>> The old implementation suffers from the same problem that other
>> SFINAE-friendly call wrappers have which is solved by using deducing
>> this (see p5.5 of the deducing this paper P0847R7).
>>
>>         PR libstdc++/80564
>>
>> libstdc++-v3/ChangeLog:
>>
>>         * include/std/functional (__cv_like): New.
>>         (_Bind::_Res_type): Don't define when not needed.
>>         (_Bind::__dependent): Likewise.
>>         (_Bind::_Res_type_cv): Likewise.
>>         (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>>         Define as two instead of four overloads using deducing
>>         this.
>>         * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>>         diagnostics inside headers.
>>         * testsuite/20_util/bind/ref_neg.cc: Likewise.
>>         * testsuite/20_util/bind/cv_quals_4.cc: New test.
>> ---
>>  libstdc++-v3/include/std/functional           | 81 +++++++++++++++++++
>>  .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>>  .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
>>  .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>>  4 files changed, 124 insertions(+)
>>  create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>>
>> diff --git a/libstdc++-v3/include/std/functional
>> b/libstdc++-v3/include/std/functional
>> index 1928a27d3fd6..9348356f03ce 100644
>> --- a/libstdc++-v3/include/std/functional
>> +++ b/libstdc++-v3/include/std/functional
>> @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>  # define _GLIBCXX_DEPR_BIND
>>  #endif
>>
>> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>> +  // Return a _Up that has the same cv-quals as _Tp.
>> +  template<typename _Tp, typename _Up>
>> +    struct __cv_like
>> +    { using type = _Up; };
>> +
>> +  template<typename _Tp, typename _Up>
>> +    struct __cv_like<const _Tp, _Up>
>> +    { using type = const _Up; };
>> +
>> +  template<typename _Tp, typename _Up>
>> +    struct __cv_like<volatile _Tp, _Up>
>> +    { using type = volatile _Up; };
>> +
>> +  template<typename _Tp, typename _Up>
>> +    struct __cv_like<const volatile _Tp, _Up>
>> +    { using type = const volatile _Up; };
>> +
>> +  template<typename _Tp, typename _Up>
>> +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
>> +#endif
>> +
>>    /// Type of the function object returned from bind().
>>    template<typename _Signature>
>>      class _Bind;
>> @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>         using _Res_type_impl
>>           = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
>>
>> +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>>        template<typename _CallArgs>
>>         using _Res_type = _Res_type_impl<_Functor, _CallArgs,
>> _Bound_args...>;
>>
>> @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>           typename __cv_quals<__dependent<_CallArgs>>::type,
>>           _CallArgs,
>>           typename __cv_quals<_Bound_args>::type...>;
>> +#endif
>>
>>       public:
>>        template<typename... _Args>
>> @@ -593,6 +617,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>        _Bind(const _Bind&) = default;
>>        _Bind(_Bind&&) = default;
>>
>> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>> +# pragma GCC diagnostic push
>> +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>> +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
>> +      // Call unqualified
>> +      template<typename... _Args,
>> +              typename _Self,
>> +              typename _Self_nonref = typename
>> remove_reference<_Self>::type,
>> +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
>> +              typename _Result
>> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
>> +                                 tuple<_Args...>,
>> +                                 __cv_like_t<_Self_nonref,
>> _Bound_args>...>>
>> +       _GLIBCXX20_CONSTEXPR
>> +       _Result
>> +       operator()(this _Self&& __self, _Args&&... __args)
>> +       {
>> +         if constexpr (is_const<_Self_nonref>::value)
>> +           return static_cast<const _Bind&>(__self)
>>
> Once you have __cv_like_t, is there any reason to not use something like
> here:
>       using _Bind_ref_t = __cv_like_t<_Self_nonref, _Bind>&;
>         _Bind_ref_t(__self).
> I think we also need to use c-cast, if someone privately inherited from
> bind specialization,
> for some reason. I thinking of version of overload:
> template<typename... Funcs>
> class Overload : Funcs....
> {
> public:
>      Overload(Funcs... func) : Funcs(funcs)... {}
>      using Funcs::operator()....;
> };
>
> And then used with bind.
>
Could you add a test like this.

> +             .template __call_c<_Result>(std::forward_as_tuple
>> +
>>  (std::forward<_Args>(__args)...),
>> +                                         _Bound_indexes());
>> +         else
>> +           return static_cast<_Bind&>(__self)
>> +             .template __call<_Result>(std::forward_as_tuple
>> +                                       (std::forward<_Args>(__args)...),
>> +                                       _Bound_indexes());
>> +       }
>> +
>> +# if defined(_GLIBCXX_VOLATILE_BIND)
>> +      template<typename... _Args,
>> +              typename _Self,
>> +              typename _Self_nonref = typename
>> remove_reference<_Self>::type,
>> +              __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
>> +              typename _Result
>> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
>> +                                 tuple<_Args...>,
>> +                                 __cv_like_t<_Self_nonref,
>> _Bound_args>...>>
>> +       _GLIBCXX_DEPR_BIND
>> +       _Result
>> +       operator()(this _Self&& __self, _Args&&... __args)
>> +       {
>> +         if constexpr (is_const<_Self_nonref>::value)
>> +           return static_cast<const volatile _Bind&>(__self)
>> +             .template __call_c_v<_Result>(std::forward_as_tuple
>> +
>>  (std::forward<_Args>(__args)...),
>> +                                           _Bound_indexes());
>> +         else
>> +           return static_cast<volatile _Bind&>(__self)
>> +             .template __call_v<_Result>(std::forward_as_tuple
>> +
>>  (std::forward<_Args>(__args)...),
>> +                                         _Bound_indexes());
>> +       }
>> +# endif
>> +# pragma GCC diagnostic pop
>> +#else
>>        // Call unqualified
>>        template<typename... _Args,
>>                typename _Result = _Res_type<tuple<_Args...>>>
>> @@ -642,6 +722,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>               _Bound_indexes());
>>         }
>>  #endif // volatile
>> +#endif
>>      };
>>
>>    /// Type of the function object returned from bind<R>().
>> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>> index e4c348f7a3ca..6d37cc43fd3a 100644
>> --- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>> @@ -44,6 +44,9 @@ void test01()
>>                        // { dg-error "no match" "" { target c++20 } 43 }
>>  }
>>
>> +// Ignore the reasons for deduction/substitution failure in the headers.
>> +// { dg-prune-output "no type named 'type' in 'struct
>> std::enable_if<false" }
>> +
>>  int main()
>>  {
>>    test01();
>> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>> new file mode 100644
>> index 000000000000..365a6ff4e00f
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>> @@ -0,0 +1,39 @@
>> +// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
>> +// { dg-do compile { target c++14 } }
>> +
>> +#include <functional>
>> +
>> +struct A
>> +{
>> +  template<class T>
>> +  auto operator()(T&)
>> +  { }
>> +
>> +  template<class T>
>> +  auto operator()(T&) const
>> +  { T::fail; }
>> +};
>> +
>> +void
>> +test01()
>> +{
>> +  A a;
>> +  std::bind(a, 0)(); // doesn't consider the const overload
>> +  std::bind<void>(a, 0)();
>> +}
>> +
>> +void
>> +test02()
>> +{
>> +  auto f = [] (auto& x) { x = 1; };
>> +  int i;
>> +  std::bind(f, i)(); // doesn't try const-invoking the lambda
>> +  std::bind<void>(f, i)();
>> +}
>> +
>> +int
>> +main()
>> +{
>> +  test01();
>> +  test02();
>> +}
>> diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>> b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>> index dd47c437d426..46cc4bb330e2 100644
>> --- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>> +++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>> @@ -51,6 +51,7 @@ void test02()
>>  // Ignore the reasons for deduction/substitution failure in the headers.
>>  // Arrange for the match to work on installed trees as well as build
>> trees.
>>  // { dg-prune-output "no type named 'type' in 'struct
>> std::__invoke_result" }
>> +// { dg-prune-output "no type named 'type' in 'struct
>> std::enable_if<false" }
>>
>>  int main()
>>  {
>> --
>> 2.52.0.154.gf0ef5b6d9b
>>
>>
  
Patrick Palka Dec. 5, 2025, 3:26 p.m. UTC | #3
On Fri, 5 Dec 2025, Tomasz Kaminski wrote:

> Just one small suggestion, otherwise LGTM.
> 
> On Fri, Dec 5, 2025 at 3:57 PM Patrick Palka <ppalka@redhat.com> wrote:
>       Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?
> 
>       -- >8 --
> 
>       Implement the cv-qual forwarding required by std::bind using deducing
>       this when available, instead of needing 4 operator() overloads.  Using
>       deducing this here is more complicated here than in other call
>       wrappers because std::bind is not really "perfect forwarding": it
>       doesn't forward value category, and along with const-ness it also
>       forwards volatile-ness (before C++20).
> 
>       The old implementation suffers from the same problem that other
>       SFINAE-friendly call wrappers have which is solved by using deducing
>       this (see p5.5 of the deducing this paper P0847R7).
> 
>               PR libstdc++/80564
> 
>       libstdc++-v3/ChangeLog:
> 
>               * include/std/functional (__cv_like): New.
>               (_Bind::_Res_type): Don't define when not needed.
>               (_Bind::__dependent): Likewise.
>               (_Bind::_Res_type_cv): Likewise.
>               (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>               Define as two instead of four overloads using deducing
>               this.
>               * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>               diagnostics inside headers.
>               * testsuite/20_util/bind/ref_neg.cc: Likewise.
>               * testsuite/20_util/bind/cv_quals_4.cc: New test.
>       ---
>        libstdc++-v3/include/std/functional           | 81 +++++++++++++++++++
>        .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>        .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
>        .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>        4 files changed, 124 insertions(+)
>        create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> 
>       diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
>       index 1928a27d3fd6..9348356f03ce 100644
>       --- a/libstdc++-v3/include/std/functional
>       +++ b/libstdc++-v3/include/std/functional
>       @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        # define _GLIBCXX_DEPR_BIND
>        #endif
> 
>       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>       +  // Return a _Up that has the same cv-quals as _Tp.
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like
>       +    { using type = _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like<const _Tp, _Up>
>       +    { using type = const _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like<volatile _Tp, _Up>
>       +    { using type = volatile _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like<const volatile _Tp, _Up>
>       +    { using type = const volatile _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
>       +#endif
>       +
>          /// Type of the function object returned from bind().
>          template<typename _Signature>
>            class _Bind;
>       @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               using _Res_type_impl
>                 = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
> 
>       +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>              template<typename _CallArgs>
>               using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
> 
>       @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                 typename __cv_quals<__dependent<_CallArgs>>::type,
>                 _CallArgs,
>                 typename __cv_quals<_Bound_args>::type...>;
>       +#endif
> 
>             public:
>              template<typename... _Args>
>       @@ -593,6 +617,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>              _Bind(const _Bind&) = default;
>              _Bind(_Bind&&) = default;
> 
>       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>       +# pragma GCC diagnostic push
>       +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>       +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
>       +      // Call unqualified
>       +      template<typename... _Args,
>       +              typename _Self,
>       +              typename _Self_nonref = typename remove_reference<_Self>::type,
>       +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
>       +              typename _Result
>       +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
>       +                                 tuple<_Args...>,
>       +                                 __cv_like_t<_Self_nonref, _Bound_args>...>>
>       +       _GLIBCXX20_CONSTEXPR
>       +       _Result
>       +       operator()(this _Self&& __self, _Args&&... __args)
>       +       {
>       +         if constexpr (is_const<_Self_nonref>::value)
>       +           return static_cast<const _Bind&>(__self)
> 
> Once you have __cv_like_t, is there any reason to not use something like here:
>       using _Bind_ref_t = __cv_like_t<_Self_nonref, _Bind>&;
>         _Bind_ref_t(__self).
> I think we also need to use c-cast, if someone privately inherited from bind specialization,
> for some reason. I thinking of version of overload:
> template<typename... Funcs>
> class Overload : Funcs....
> {
> public: 
>      Overload(Funcs... func) : Funcs(funcs)... {}
>      using Funcs::operator()....;
> };
> 
> And then used with bind.

Good catch, I keep forgetting we need a C-style cast with deducing
this.

Changes in v2:
- use a C-style cast instead of static_cast when casting explicit
  this parameter to the curret instantiation, and add test for that

Testing on x86_64-pc-ilnux-gnu in progress, OK for trunk once
passes?

-- >8 --

Subject: [PATCH] libstdc++: Use deducing this in std::bind when available
 [PR80564]

Implement the cv-qual forwarding required by std::bind using deducing
this when available, instead of needing 4 operator() overloads.  Using
deducing this here is more complicated here than in other call
wrappers because std::bind is not really "perfect forwarding": it
doesn't forward value category, and along with const-ness it also
forwards volatile-ness (before C++20).

The old implementation suffers from the same problem that other
SFINAE-friendly call wrappers have which is solved by using deducing
this (see p5.5 of the deducing this paper P0847R7).

	PR libstdc++/80564

libstdc++-v3/ChangeLog:

	* include/std/functional (__cv_like): New.
	(_Bind::_Res_type): Don't define when not needed.
	(_Bind::__dependent): Likewise.
	(_Bind::_Res_type_cv): Likewise.
	(_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
	Define as two instead of four overloads using deducing
	this.
	* testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
	diagnostics inside headers.
	* testsuite/20_util/bind/ref_neg.cc: Likewise.
	* testsuite/20_util/bind/cv_quals_4.cc: New test.
---
 libstdc++-v3/include/std/functional           | 82 +++++++++++++++++++
 .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
 .../testsuite/20_util/bind/cv_quals_4.cc      | 50 +++++++++++
 .../testsuite/20_util/bind/ref_neg.cc         |  1 +
 4 files changed, 136 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc

diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 1928a27d3fd6..06ff32752d4a 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # define _GLIBCXX_DEPR_BIND
 #endif
 
+#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
+  // Return a _Up that has the same cv-quals as _Tp.
+  template<typename _Tp, typename _Up> // _Up should be cv-unqualified
+    struct __cv_like
+    { using type = _Up; };
+
+  template<typename _Tp, typename _Up>
+    struct __cv_like<const _Tp, _Up>
+    { using type = const _Up; };
+
+  template<typename _Tp, typename _Up>
+    struct __cv_like<volatile _Tp, _Up>
+    { using type = volatile _Up; };
+
+  template<typename _Tp, typename _Up>
+    struct __cv_like<const volatile _Tp, _Up>
+    { using type = const volatile _Up; };
+
+  template<typename _Tp, typename _Up>
+    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
+#endif
+
   /// Type of the function object returned from bind().
   template<typename _Signature>
     class _Bind;
@@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	using _Res_type_impl
 	  = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
 
+#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
       template<typename _CallArgs>
 	using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
 
@@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  typename __cv_quals<__dependent<_CallArgs>>::type,
 	  _CallArgs,
 	  typename __cv_quals<_Bound_args>::type...>;
+#endif
 
      public:
       template<typename... _Args>
@@ -593,6 +617,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Bind(const _Bind&) = default;
       _Bind(_Bind&&) = default;
 
+#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
+      template<typename... _Args,
+	       typename _Self,
+	       typename _Self_nonref = typename remove_reference<_Self>::type,
+	       __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
+	       typename _Result
+		 = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
+				  tuple<_Args...>,
+				  __cv_like_t<_Self_nonref, _Bound_args>...>>
+	_GLIBCXX20_CONSTEXPR
+	_Result
+	operator()(this _Self&& __self, _Args&&... __args)
+	{
+	  using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
+	  if constexpr (is_const<_Self_nonref>::value)
+	    return _Bind_ref(__self)
+	      .template __call_c<_Result>(std::forward_as_tuple
+					  (std::forward<_Args>(__args)...),
+					  _Bound_indexes());
+	  else
+	    return _Bind_ref(__self)
+	      .template __call<_Result>(std::forward_as_tuple
+					(std::forward<_Args>(__args)...),
+					_Bound_indexes());
+	}
+
+# if defined(_GLIBCXX_VOLATILE_BIND)
+      template<typename... _Args,
+	       typename _Self,
+	       typename _Self_nonref = typename remove_reference<_Self>::type,
+	       __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
+	       typename _Result
+		 = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
+				  tuple<_Args...>,
+				  __cv_like_t<_Self_nonref, _Bound_args>...>>
+	_GLIBCXX_DEPR_BIND
+	_Result
+	operator()(this _Self&& __self, _Args&&... __args)
+	{
+	  using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
+	  if constexpr (is_const<_Self_nonref>::value)
+	    return _Bind_ref(__self)
+	      .template __call_c_v<_Result>(std::forward_as_tuple
+					    (std::forward<_Args>(__args)...),
+					    _Bound_indexes());
+	  else
+	    return _Bind_ref(__self)
+	      .template __call_v<_Result>(std::forward_as_tuple
+					  (std::forward<_Args>(__args)...),
+					  _Bound_indexes());
+	}
+# endif
+# pragma GCC diagnostic pop
+#else
       // Call unqualified
       template<typename... _Args,
 	       typename _Result = _Res_type<tuple<_Args...>>>
@@ -642,6 +723,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      _Bound_indexes());
 	}
 #endif // volatile
+#endif
     };
 
   /// Type of the function object returned from bind<R>().
diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
index e4c348f7a3ca..6d37cc43fd3a 100644
--- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
@@ -44,6 +44,9 @@ void test01()
 		       // { dg-error "no match" "" { target c++20 } 43 }
 }
 
+// Ignore the reasons for deduction/substitution failure in the headers.
+// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
+
 int main()
 {
   test01();
diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
new file mode 100644
index 000000000000..f6e1a1ec66da
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
@@ -0,0 +1,50 @@
+// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
+// { dg-do compile { target c++14 } }
+
+#include <functional>
+
+struct A
+{
+  template<typename T>
+  auto operator()(T&)
+  { }
+
+  template<typename T>
+  auto operator()(T&) const
+  { T::fail; }
+};
+
+void
+test01()
+{
+  A a;
+  std::bind(a, 0)(); // doesn't consider the const overload
+  std::bind<void>(a, 0)();
+}
+
+void
+test02()
+{
+  auto f = [] (auto& x) { x = 1; };
+  int i;
+  std::bind(f, i)(); // doesn't try const-invoking the lambda
+  std::bind<void>(f, i)();
+}
+
+#if __cpp_variadic_using
+template<typename... Ts>
+struct overloaded : private Ts...
+{
+  overloaded(Ts... ts) : Ts(ts)... { }
+  using Ts::operator()...;
+};
+
+void
+test03()
+{
+  A a;
+  auto f = std::bind(a, 0);
+  overloaded<decltype(f)> g(f);
+  g();
+}
+#endif
diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
index dd47c437d426..46cc4bb330e2 100644
--- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
@@ -51,6 +51,7 @@ void test02()
 // Ignore the reasons for deduction/substitution failure in the headers.
 // Arrange for the match to work on installed trees as well as build trees.
 // { dg-prune-output "no type named 'type' in 'struct std::__invoke_result" }
+// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
 
 int main()
 {
  
Tomasz Kamiński Dec. 5, 2025, 3:35 p.m. UTC | #4
On Fri, Dec 5, 2025 at 4:26 PM Patrick Palka <ppalka@redhat.com> wrote:

> On Fri, 5 Dec 2025, Tomasz Kaminski wrote:
>
> > Just one small suggestion, otherwise LGTM.
> >
> > On Fri, Dec 5, 2025 at 3:57 PM Patrick Palka <ppalka@redhat.com> wrote:
> >       Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?
> >
> >       -- >8 --
> >
> >       Implement the cv-qual forwarding required by std::bind using
> deducing
> >       this when available, instead of needing 4 operator() overloads.
> Using
> >       deducing this here is more complicated here than in other call
> >       wrappers because std::bind is not really "perfect forwarding": it
> >       doesn't forward value category, and along with const-ness it also
> >       forwards volatile-ness (before C++20).
> >
> >       The old implementation suffers from the same problem that other
> >       SFINAE-friendly call wrappers have which is solved by using
> deducing
> >       this (see p5.5 of the deducing this paper P0847R7).
> >
> >               PR libstdc++/80564
> >
> >       libstdc++-v3/ChangeLog:
> >
> >               * include/std/functional (__cv_like): New.
> >               (_Bind::_Res_type): Don't define when not needed.
> >               (_Bind::__dependent): Likewise.
> >               (_Bind::_Res_type_cv): Likewise.
> >               (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
> >               Define as two instead of four overloads using deducing
> >               this.
> >               * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
> >               diagnostics inside headers.
> >               * testsuite/20_util/bind/ref_neg.cc: Likewise.
> >               * testsuite/20_util/bind/cv_quals_4.cc: New test.
> >       ---
> >        libstdc++-v3/include/std/functional           | 81
> +++++++++++++++++++
> >        .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
> >        .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
> >        .../testsuite/20_util/bind/ref_neg.cc         |  1 +
> >        4 files changed, 124 insertions(+)
> >        create mode 100644
> libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> >
> >       diff --git a/libstdc++-v3/include/std/functional
> b/libstdc++-v3/include/std/functional
> >       index 1928a27d3fd6..9348356f03ce 100644
> >       --- a/libstdc++-v3/include/std/functional
> >       +++ b/libstdc++-v3/include/std/functional
> >       @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        # define _GLIBCXX_DEPR_BIND
> >        #endif
> >
> >       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> >       +  // Return a _Up that has the same cv-quals as _Tp.
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like
> >       +    { using type = _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like<const _Tp, _Up>
> >       +    { using type = const _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like<volatile _Tp, _Up>
> >       +    { using type = volatile _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like<const volatile _Tp, _Up>
> >       +    { using type = const volatile _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
> >       +#endif
> >       +
> >          /// Type of the function object returned from bind().
> >          template<typename _Signature>
> >            class _Bind;
> >       @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >               using _Res_type_impl
> >                 = __invoke_result_t<_Fn&, _Mu_type<_BArgs,
> _CallArgs>&&...>;
> >
> >       +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
> >              template<typename _CallArgs>
> >               using _Res_type = _Res_type_impl<_Functor, _CallArgs,
> _Bound_args...>;
> >
> >       @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >                 typename __cv_quals<__dependent<_CallArgs>>::type,
> >                 _CallArgs,
> >                 typename __cv_quals<_Bound_args>::type...>;
> >       +#endif
> >
> >             public:
> >              template<typename... _Args>
> >       @@ -593,6 +617,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >              _Bind(const _Bind&) = default;
> >              _Bind(_Bind&&) = default;
> >
> >       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> >       +# pragma GCC diagnostic push
> >       +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if
> constexpr
> >       +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing
> this
> >       +      // Call unqualified
> >       +      template<typename... _Args,
> >       +              typename _Self,
> >       +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> >       +              __enable_if_t<!is_volatile<_Self_nonref>::value,
> int> = 0,
> >       +              typename _Result
> >       +                = _Res_type_impl<__cv_like_t<_Self_nonref,
> _Functor>,
> >       +                                 tuple<_Args...>,
> >       +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> >       +       _GLIBCXX20_CONSTEXPR
> >       +       _Result
> >       +       operator()(this _Self&& __self, _Args&&... __args)
> >       +       {
> >       +         if constexpr (is_const<_Self_nonref>::value)
> >       +           return static_cast<const _Bind&>(__self)
> >
> > Once you have __cv_like_t, is there any reason to not use something like
> here:
> >       using _Bind_ref_t = __cv_like_t<_Self_nonref, _Bind>&;
> >         _Bind_ref_t(__self).
> > I think we also need to use c-cast, if someone privately inherited from
> bind specialization,
> > for some reason. I thinking of version of overload:
> > template<typename... Funcs>
> > class Overload : Funcs....
> > {
> > public:
> >      Overload(Funcs... func) : Funcs(funcs)... {}
> >      using Funcs::operator()....;
> > };
> >
> > And then used with bind.
>
> Good catch, I keep forgetting we need a C-style cast with deducing
> this.
>
> Changes in v2:
> - use a C-style cast instead of static_cast when casting explicit
>   this parameter to the curret instantiation, and add test for that
>
> Testing on x86_64-pc-ilnux-gnu in progress, OK for trunk once
> passes?
>
LGTM.

>
> -- >8 --
>
> Subject: [PATCH] libstdc++: Use deducing this in std::bind when available
>  [PR80564]
>
> Implement the cv-qual forwarding required by std::bind using deducing
> this when available, instead of needing 4 operator() overloads.  Using
> deducing this here is more complicated here than in other call
> wrappers because std::bind is not really "perfect forwarding": it
> doesn't forward value category, and along with const-ness it also
> forwards volatile-ness (before C++20).
>
> The old implementation suffers from the same problem that other
> SFINAE-friendly call wrappers have which is solved by using deducing
> this (see p5.5 of the deducing this paper P0847R7).
>
>         PR libstdc++/80564
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/functional (__cv_like): New.
>         (_Bind::_Res_type): Don't define when not needed.
>         (_Bind::__dependent): Likewise.
>         (_Bind::_Res_type_cv): Likewise.
>         (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>         Define as two instead of four overloads using deducing
>         this.
>         * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>         diagnostics inside headers.
>         * testsuite/20_util/bind/ref_neg.cc: Likewise.
>         * testsuite/20_util/bind/cv_quals_4.cc: New test.
> ---
>  libstdc++-v3/include/std/functional           | 82 +++++++++++++++++++
>  .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>  .../testsuite/20_util/bind/cv_quals_4.cc      | 50 +++++++++++
>  .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>  4 files changed, 136 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>
> diff --git a/libstdc++-v3/include/std/functional
> b/libstdc++-v3/include/std/functional
> index 1928a27d3fd6..06ff32752d4a 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  # define _GLIBCXX_DEPR_BIND
>  #endif
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +  // Return a _Up that has the same cv-quals as _Tp.
> +  template<typename _Tp, typename _Up> // _Up should be cv-unqualified
> +    struct __cv_like
> +    { using type = _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<const _Tp, _Up>
> +    { using type = const _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<volatile _Tp, _Up>
> +    { using type = volatile _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<const volatile _Tp, _Up>
> +    { using type = const volatile _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
> +#endif
> +
>    /// Type of the function object returned from bind().
>    template<typename _Signature>
>      class _Bind;
> @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         using _Res_type_impl
>           = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
>
> +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>        template<typename _CallArgs>
>         using _Res_type = _Res_type_impl<_Functor, _CallArgs,
> _Bound_args...>;
>
> @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           typename __cv_quals<__dependent<_CallArgs>>::type,
>           _CallArgs,
>           typename __cv_quals<_Bound_args>::type...>;
> +#endif
>
>       public:
>        template<typename... _Args>
> @@ -593,6 +617,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _Bind(const _Bind&) = default;
>        _Bind(_Bind&&) = default;
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +# pragma GCC diagnostic push
> +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
> +      template<typename... _Args,
> +              typename _Self,
> +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
> +              typename _Result
> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
> +                                 tuple<_Args...>,
> +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> +       _GLIBCXX20_CONSTEXPR
> +       _Result
> +       operator()(this _Self&& __self, _Args&&... __args)
> +       {
> +         using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
> +         if constexpr (is_const<_Self_nonref>::value)
> +           return _Bind_ref(__self)
> +             .template __call_c<_Result>(std::forward_as_tuple
> +                                         (std::forward<_Args>(__args)...),
> +                                         _Bound_indexes());
> +         else
> +           return _Bind_ref(__self)
> +             .template __call<_Result>(std::forward_as_tuple
> +                                       (std::forward<_Args>(__args)...),
> +                                       _Bound_indexes());
> +       }
> +
> +# if defined(_GLIBCXX_VOLATILE_BIND)
> +      template<typename... _Args,
> +              typename _Self,
> +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> +              __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
> +              typename _Result
> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
> +                                 tuple<_Args...>,
> +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> +       _GLIBCXX_DEPR_BIND
> +       _Result
> +       operator()(this _Self&& __self, _Args&&... __args)
> +       {
> +         using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
> +         if constexpr (is_const<_Self_nonref>::value)
> +           return _Bind_ref(__self)
> +             .template __call_c_v<_Result>(std::forward_as_tuple
> +
>  (std::forward<_Args>(__args)...),
> +                                           _Bound_indexes());
> +         else
> +           return _Bind_ref(__self)
> +             .template __call_v<_Result>(std::forward_as_tuple
> +                                         (std::forward<_Args>(__args)...),
> +                                         _Bound_indexes());
> +       }
> +# endif
> +# pragma GCC diagnostic pop
> +#else
>        // Call unqualified
>        template<typename... _Args,
>                typename _Result = _Res_type<tuple<_Args...>>>
> @@ -642,6 +723,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               _Bound_indexes());
>         }
>  #endif // volatile
> +#endif
>      };
>
>    /// Type of the function object returned from bind<R>().
> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> index e4c348f7a3ca..6d37cc43fd3a 100644
> --- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> @@ -44,6 +44,9 @@ void test01()
>                        // { dg-error "no match" "" { target c++20 } 43 }
>  }
>
> +// Ignore the reasons for deduction/substitution failure in the headers.
> +// { dg-prune-output "no type named 'type' in 'struct
> std::enable_if<false" }
> +
>  int main()
>  {
>    test01();
> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> new file mode 100644
> index 000000000000..f6e1a1ec66da
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> @@ -0,0 +1,50 @@
> +// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
> +// { dg-do compile { target c++14 } }
> +
> +#include <functional>
> +
> +struct A
> +{
> +  template<typename T>
> +  auto operator()(T&)
> +  { }
> +
> +  template<typename T>
> +  auto operator()(T&) const
> +  { T::fail; }
> +};
> +
> +void
> +test01()
> +{
> +  A a;
> +  std::bind(a, 0)(); // doesn't consider the const overload
> +  std::bind<void>(a, 0)();
> +}
> +
> +void
> +test02()
> +{
> +  auto f = [] (auto& x) { x = 1; };
> +  int i;
> +  std::bind(f, i)(); // doesn't try const-invoking the lambda
> +  std::bind<void>(f, i)();
> +}
> +
> +#if __cpp_variadic_using
> +template<typename... Ts>
> +struct overloaded : private Ts...
> +{
> +  overloaded(Ts... ts) : Ts(ts)... { }
> +  using Ts::operator()...;
> +};
> +
> +void
> +test03()
> +{
> +  A a;
> +  auto f = std::bind(a, 0);
> +  overloaded<decltype(f)> g(f);
> +  g();
> +}
> +#endif
> diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> index dd47c437d426..46cc4bb330e2 100644
> --- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> @@ -51,6 +51,7 @@ void test02()
>  // Ignore the reasons for deduction/substitution failure in the headers.
>  // Arrange for the match to work on installed trees as well as build
> trees.
>  // { dg-prune-output "no type named 'type' in 'struct
> std::__invoke_result" }
> +// { dg-prune-output "no type named 'type' in 'struct
> std::enable_if<false" }
>
>  int main()
>  {
> --
> 2.52.0.154.gf0ef5b6d9b
>
  
Patrick Palka Dec. 5, 2025, 3:54 p.m. UTC | #5
On Fri, 5 Dec 2025, Tomasz Kaminski wrote:

> 
> 
> On Fri, Dec 5, 2025 at 4:26 PM Patrick Palka <ppalka@redhat.com> wrote:
>       On Fri, 5 Dec 2025, Tomasz Kaminski wrote:
> 
>       > Just one small suggestion, otherwise LGTM.
>       >
>       > On Fri, Dec 5, 2025 at 3:57 PM Patrick Palka <ppalka@redhat.com> wrote:
>       >       Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?
>       >
>       >       -- >8 --
>       >
>       >       Implement the cv-qual forwarding required by std::bind using deducing
>       >       this when available, instead of needing 4 operator() overloads.  Using
>       >       deducing this here is more complicated here than in other call
>       >       wrappers because std::bind is not really "perfect forwarding": it
>       >       doesn't forward value category, and along with const-ness it also
>       >       forwards volatile-ness (before C++20).
>       >
>       >       The old implementation suffers from the same problem that other
>       >       SFINAE-friendly call wrappers have which is solved by using deducing
>       >       this (see p5.5 of the deducing this paper P0847R7).
>       >
>       >               PR libstdc++/80564
>       >
>       >       libstdc++-v3/ChangeLog:
>       >
>       >               * include/std/functional (__cv_like): New.
>       >               (_Bind::_Res_type): Don't define when not needed.
>       >               (_Bind::__dependent): Likewise.
>       >               (_Bind::_Res_type_cv): Likewise.
>       >               (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>       >               Define as two instead of four overloads using deducing
>       >               this.
>       >               * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>       >               diagnostics inside headers.
>       >               * testsuite/20_util/bind/ref_neg.cc: Likewise.
>       >               * testsuite/20_util/bind/cv_quals_4.cc: New test.
>       >       ---
>       >        libstdc++-v3/include/std/functional           | 81 +++++++++++++++++++
>       >        .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>       >        .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
>       >        .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>       >        4 files changed, 124 insertions(+)
>       >        create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>       >
>       >       diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
>       >       index 1928a27d3fd6..9348356f03ce 100644
>       >       --- a/libstdc++-v3/include/std/functional
>       >       +++ b/libstdc++-v3/include/std/functional
>       >       @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >        # define _GLIBCXX_DEPR_BIND
>       >        #endif
>       >
>       >       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>       >       +  // Return a _Up that has the same cv-quals as _Tp.
>       >       +  template<typename _Tp, typename _Up>
>       >       +    struct __cv_like
>       >       +    { using type = _Up; };
>       >       +
>       >       +  template<typename _Tp, typename _Up>
>       >       +    struct __cv_like<const _Tp, _Up>
>       >       +    { using type = const _Up; };
>       >       +
>       >       +  template<typename _Tp, typename _Up>
>       >       +    struct __cv_like<volatile _Tp, _Up>
>       >       +    { using type = volatile _Up; };
>       >       +
>       >       +  template<typename _Tp, typename _Up>
>       >       +    struct __cv_like<const volatile _Tp, _Up>
>       >       +    { using type = const volatile _Up; };
>       >       +
>       >       +  template<typename _Tp, typename _Up>
>       >       +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
>       >       +#endif
>       >       +
>       >          /// Type of the function object returned from bind().
>       >          template<typename _Signature>
>       >            class _Bind;
>       >       @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >               using _Res_type_impl
>       >                 = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
>       >
>       >       +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>       >              template<typename _CallArgs>
>       >               using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
>       >
>       >       @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >                 typename __cv_quals<__dependent<_CallArgs>>::type,
>       >                 _CallArgs,
>       >                 typename __cv_quals<_Bound_args>::type...>;
>       >       +#endif
>       >
>       >             public:
>       >              template<typename... _Args>
>       >       @@ -593,6 +617,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >              _Bind(const _Bind&) = default;
>       >              _Bind(_Bind&&) = default;
>       >
>       >       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>       >       +# pragma GCC diagnostic push
>       >       +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>       >       +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
>       >       +      // Call unqualified
>       >       +      template<typename... _Args,
>       >       +              typename _Self,
>       >       +              typename _Self_nonref = typename remove_reference<_Self>::type,
>       >       +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
>       >       +              typename _Result
>       >       +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
>       >       +                                 tuple<_Args...>,
>       >       +                                 __cv_like_t<_Self_nonref, _Bound_args>...>>
>       >       +       _GLIBCXX20_CONSTEXPR
>       >       +       _Result
>       >       +       operator()(this _Self&& __self, _Args&&... __args)
>       >       +       {
>       >       +         if constexpr (is_const<_Self_nonref>::value)
>       >       +           return static_cast<const _Bind&>(__self)
>       >
>       > Once you have __cv_like_t, is there any reason to not use something like here:
>       >       using _Bind_ref_t = __cv_like_t<_Self_nonref, _Bind>&;
>       >         _Bind_ref_t(__self).
>       > I think we also need to use c-cast, if someone privately inherited from bind specialization,
>       > for some reason. I thinking of version of overload:
>       > template<typename... Funcs>
>       > class Overload : Funcs....
>       > {
>       > public: 
>       >      Overload(Funcs... func) : Funcs(funcs)... {}
>       >      using Funcs::operator()....;
>       > };
>       >
>       > And then used with bind.
> 
>       Good catch, I keep forgetting we need a C-style cast with deducing
>       this.
> 
>       Changes in v2:
>       - use a C-style cast instead of static_cast when casting explicit
>         this parameter to the curret instantiation, and add test for that
> 
>       Testing on x86_64-pc-ilnux-gnu in progress, OK for trunk once
>       passes?
> 
> LGTM. 

Thanks!

> 
>       -- >8 --
> 
>       Subject: [PATCH] libstdc++: Use deducing this in std::bind when available
>        [PR80564]
> 
>       Implement the cv-qual forwarding required by std::bind using deducing
>       this when available, instead of needing 4 operator() overloads.  Using
>       deducing this here is more complicated here than in other call
>       wrappers because std::bind is not really "perfect forwarding": it
>       doesn't forward value category, and along with const-ness it also
>       forwards volatile-ness (before C++20).
> 
>       The old implementation suffers from the same problem that other
>       SFINAE-friendly call wrappers have which is solved by using deducing
>       this (see p5.5 of the deducing this paper P0847R7).
> 
>               PR libstdc++/80564
> 
>       libstdc++-v3/ChangeLog:
> 
>               * include/std/functional (__cv_like): New.
>               (_Bind::_Res_type): Don't define when not needed.
>               (_Bind::__dependent): Likewise.
>               (_Bind::_Res_type_cv): Likewise.
>               (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>               Define as two instead of four overloads using deducing
>               this.
>               * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>               diagnostics inside headers.
>               * testsuite/20_util/bind/ref_neg.cc: Likewise.
>               * testsuite/20_util/bind/cv_quals_4.cc: New test.
>       ---
>        libstdc++-v3/include/std/functional           | 82 +++++++++++++++++++
>        .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>        .../testsuite/20_util/bind/cv_quals_4.cc      | 50 +++++++++++
>        .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>        4 files changed, 136 insertions(+)
>        create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> 
>       diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
>       index 1928a27d3fd6..06ff32752d4a 100644
>       --- a/libstdc++-v3/include/std/functional
>       +++ b/libstdc++-v3/include/std/functional
>       @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        # define _GLIBCXX_DEPR_BIND
>        #endif
> 
>       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>       +  // Return a _Up that has the same cv-quals as _Tp.
>       +  template<typename _Tp, typename _Up> // _Up should be cv-unqualified
>       +    struct __cv_like
>       +    { using type = _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like<const _Tp, _Up>
>       +    { using type = const _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like<volatile _Tp, _Up>
>       +    { using type = volatile _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    struct __cv_like<const volatile _Tp, _Up>
>       +    { using type = const volatile _Up; };
>       +
>       +  template<typename _Tp, typename _Up>
>       +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
>       +#endif
>       +
>          /// Type of the function object returned from bind().
>          template<typename _Signature>
>            class _Bind;
>       @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               using _Res_type_impl
>                 = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
> 
>       +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>              template<typename _CallArgs>
>               using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
> 
>       @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                 typename __cv_quals<__dependent<_CallArgs>>::type,
>                 _CallArgs,
>                 typename __cv_quals<_Bound_args>::type...>;
>       +#endif
> 
>             public:
>              template<typename... _Args>
>       @@ -593,6 +617,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>              _Bind(const _Bind&) = default;
>              _Bind(_Bind&&) = default;
> 
>       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
>       +# pragma GCC diagnostic push
>       +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>       +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
>       +      template<typename... _Args,
>       +              typename _Self,
>       +              typename _Self_nonref = typename remove_reference<_Self>::type,
>       +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
>       +              typename _Result
>       +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
>       +                                 tuple<_Args...>,
>       +                                 __cv_like_t<_Self_nonref, _Bound_args>...>>
>       +       _GLIBCXX20_CONSTEXPR
>       +       _Result
>       +       operator()(this _Self&& __self, _Args&&... __args)
>       +       {
>       +         using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
>       +         if constexpr (is_const<_Self_nonref>::value)
>       +           return _Bind_ref(__self)
>       +             .template __call_c<_Result>(std::forward_as_tuple
>       +                                         (std::forward<_Args>(__args)...),
>       +                                         _Bound_indexes());
>       +         else
>       +           return _Bind_ref(__self)
>       +             .template __call<_Result>(std::forward_as_tuple
>       +                                       (std::forward<_Args>(__args)...),
>       +                                       _Bound_indexes());
>       +       }
>       +
>       +# if defined(_GLIBCXX_VOLATILE_BIND)
>       +      template<typename... _Args,
>       +              typename _Self,
>       +              typename _Self_nonref = typename remove_reference<_Self>::type,
>       +              __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
>       +              typename _Result
>       +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
>       +                                 tuple<_Args...>,
>       +                                 __cv_like_t<_Self_nonref, _Bound_args>...>>

By the way the reason we need this separate overload for the volatile
case is solely to emit a deprecation diagnostic in C++17 mode, otherwise
we could get away with just one overload in all modes. (In C++20
the volatile overloads got removed.) So in theory we could have just one
overload before C++17, since we don't need a deprecation message in that
case, but that'd result in more complexity and code duplication as the
main overload would need to forward to __call_c_v and __call_v as well
and doesn't seem worth it.

>       +       _GLIBCXX_DEPR_BIND
>       +       _Result
>       +       operator()(this _Self&& __self, _Args&&... __args)
>       +       {
>       +         using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
>       +         if constexpr (is_const<_Self_nonref>::value)
>       +           return _Bind_ref(__self)
>       +             .template __call_c_v<_Result>(std::forward_as_tuple
>       +                                           (std::forward<_Args>(__args)...),
>       +                                           _Bound_indexes());
>       +         else
>       +           return _Bind_ref(__self)
>       +             .template __call_v<_Result>(std::forward_as_tuple
>       +                                         (std::forward<_Args>(__args)...),
>       +                                         _Bound_indexes());
>       +       }
>       +# endif
>       +# pragma GCC diagnostic pop
>       +#else
>              // Call unqualified
>              template<typename... _Args,
>                      typename _Result = _Res_type<tuple<_Args...>>>
>       @@ -642,6 +723,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                     _Bound_indexes());
>               }
>        #endif // volatile
>       +#endif
>            };
> 
>          /// Type of the function object returned from bind<R>().
>       diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>       index e4c348f7a3ca..6d37cc43fd3a 100644
>       --- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>       +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
>       @@ -44,6 +44,9 @@ void test01()
>                              // { dg-error "no match" "" { target c++20 } 43 }
>        }
> 
>       +// Ignore the reasons for deduction/substitution failure in the headers.
>       +// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
>       +
>        int main()
>        {
>          test01();
>       diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>       new file mode 100644
>       index 000000000000..f6e1a1ec66da
>       --- /dev/null
>       +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>       @@ -0,0 +1,50 @@
>       +// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
>       +// { dg-do compile { target c++14 } }
>       +
>       +#include <functional>
>       +
>       +struct A
>       +{
>       +  template<typename T>
>       +  auto operator()(T&)
>       +  { }
>       +
>       +  template<typename T>
>       +  auto operator()(T&) const
>       +  { T::fail; }
>       +};
>       +
>       +void
>       +test01()
>       +{
>       +  A a;
>       +  std::bind(a, 0)(); // doesn't consider the const overload
>       +  std::bind<void>(a, 0)();
>       +}
>       +
>       +void
>       +test02()
>       +{
>       +  auto f = [] (auto& x) { x = 1; };
>       +  int i;
>       +  std::bind(f, i)(); // doesn't try const-invoking the lambda
>       +  std::bind<void>(f, i)();
>       +}
>       +
>       +#if __cpp_variadic_using
>       +template<typename... Ts>
>       +struct overloaded : private Ts...
>       +{
>       +  overloaded(Ts... ts) : Ts(ts)... { }
>       +  using Ts::operator()...;
>       +};
>       +
>       +void
>       +test03()
>       +{
>       +  A a;
>       +  auto f = std::bind(a, 0);
>       +  overloaded<decltype(f)> g(f);
>       +  g();
>       +}
>       +#endif
>       diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>       index dd47c437d426..46cc4bb330e2 100644
>       --- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>       +++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
>       @@ -51,6 +51,7 @@ void test02()
>        // Ignore the reasons for deduction/substitution failure in the headers.
>        // Arrange for the match to work on installed trees as well as build trees.
>        // { dg-prune-output "no type named 'type' in 'struct std::__invoke_result" }
>       +// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
> 
>        int main()
>        {
>       --
>       2.52.0.154.gf0ef5b6d9b
> 
> 
>
  
Jonathan Wakely Dec. 5, 2025, 11:44 p.m. UTC | #6
On Fri, 5 Dec 2025, 15:26 Patrick Palka, <ppalka@redhat.com> wrote:

> On Fri, 5 Dec 2025, Tomasz Kaminski wrote:
>
> > Just one small suggestion, otherwise LGTM.
> >
> > On Fri, Dec 5, 2025 at 3:57 PM Patrick Palka <ppalka@redhat.com> wrote:
> >       Tested on x86_64-pc-linu-xgnu, does this look OK for trunk?
>

Nice way to fix this. OK for trunk, thanks.


>
> >       -- >8 --
> >
> >       Implement the cv-qual forwarding required by std::bind using
> deducing
> >       this when available, instead of needing 4 operator() overloads.
> Using
> >       deducing this here is more complicated here than in other call
> >       wrappers because std::bind is not really "perfect forwarding": it
> >       doesn't forward value category, and along with const-ness it also
> >       forwards volatile-ness (before C++20).
> >
> >       The old implementation suffers from the same problem that other
> >       SFINAE-friendly call wrappers have which is solved by using
> deducing
> >       this (see p5.5 of the deducing this paper P0847R7).
> >
> >               PR libstdc++/80564
> >
> >       libstdc++-v3/ChangeLog:
> >
> >               * include/std/functional (__cv_like): New.
> >               (_Bind::_Res_type): Don't define when not needed.
> >               (_Bind::__dependent): Likewise.
> >               (_Bind::_Res_type_cv): Likewise.
> >               (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
> >               Define as two instead of four overloads using deducing
> >               this.
> >               * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
> >               diagnostics inside headers.
> >               * testsuite/20_util/bind/ref_neg.cc: Likewise.
> >               * testsuite/20_util/bind/cv_quals_4.cc: New test.
> >       ---
> >        libstdc++-v3/include/std/functional           | 81
> +++++++++++++++++++
> >        .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
> >        .../testsuite/20_util/bind/cv_quals_4.cc      | 39 +++++++++
> >        .../testsuite/20_util/bind/ref_neg.cc         |  1 +
> >        4 files changed, 124 insertions(+)
> >        create mode 100644
> libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> >
> >       diff --git a/libstdc++-v3/include/std/functional
> b/libstdc++-v3/include/std/functional
> >       index 1928a27d3fd6..9348356f03ce 100644
> >       --- a/libstdc++-v3/include/std/functional
> >       +++ b/libstdc++-v3/include/std/functional
> >       @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        # define _GLIBCXX_DEPR_BIND
> >        #endif
> >
> >       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> >       +  // Return a _Up that has the same cv-quals as _Tp.
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like
> >       +    { using type = _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like<const _Tp, _Up>
> >       +    { using type = const _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like<volatile _Tp, _Up>
> >       +    { using type = volatile _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    struct __cv_like<const volatile _Tp, _Up>
> >       +    { using type = const volatile _Up; };
> >       +
> >       +  template<typename _Tp, typename _Up>
> >       +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
> >       +#endif
> >       +
> >          /// Type of the function object returned from bind().
> >          template<typename _Signature>
> >            class _Bind;
> >       @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >               using _Res_type_impl
> >                 = __invoke_result_t<_Fn&, _Mu_type<_BArgs,
> _CallArgs>&&...>;
> >
> >       +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
> >              template<typename _CallArgs>
> >               using _Res_type = _Res_type_impl<_Functor, _CallArgs,
> _Bound_args...>;
> >
> >       @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >                 typename __cv_quals<__dependent<_CallArgs>>::type,
> >                 _CallArgs,
> >                 typename __cv_quals<_Bound_args>::type...>;
> >       +#endif
> >
> >             public:
> >              template<typename... _Args>
> >       @@ -593,6 +617,62 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >              _Bind(const _Bind&) = default;
> >              _Bind(_Bind&&) = default;
> >
> >       +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> >       +# pragma GCC diagnostic push
> >       +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if
> constexpr
> >       +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing
> this
> >       +      // Call unqualified
> >       +      template<typename... _Args,
> >       +              typename _Self,
> >       +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> >       +              __enable_if_t<!is_volatile<_Self_nonref>::value,
> int> = 0,
> >       +              typename _Result
> >       +                = _Res_type_impl<__cv_like_t<_Self_nonref,
> _Functor>,
> >       +                                 tuple<_Args...>,
> >       +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> >       +       _GLIBCXX20_CONSTEXPR
> >       +       _Result
> >       +       operator()(this _Self&& __self, _Args&&... __args)
> >       +       {
> >       +         if constexpr (is_const<_Self_nonref>::value)
> >       +           return static_cast<const _Bind&>(__self)
> >
> > Once you have __cv_like_t, is there any reason to not use something like
> here:
> >       using _Bind_ref_t = __cv_like_t<_Self_nonref, _Bind>&;
> >         _Bind_ref_t(__self).
> > I think we also need to use c-cast, if someone privately inherited from
> bind specialization,
> > for some reason. I thinking of version of overload:
> > template<typename... Funcs>
> > class Overload : Funcs....
> > {
> > public:
> >      Overload(Funcs... func) : Funcs(funcs)... {}
> >      using Funcs::operator()....;
> > };
> >
> > And then used with bind.
>
> Good catch, I keep forgetting we need a C-style cast with deducing
> this.
>
> Changes in v2:
> - use a C-style cast instead of static_cast when casting explicit
>   this parameter to the curret instantiation, and add test for that
>
> Testing on x86_64-pc-ilnux-gnu in progress, OK for trunk once
> passes?
>
> -- >8 --
>
> Subject: [PATCH] libstdc++: Use deducing this in std::bind when available
>  [PR80564]
>
> Implement the cv-qual forwarding required by std::bind using deducing
> this when available, instead of needing 4 operator() overloads.  Using
> deducing this here is more complicated here than in other call
> wrappers because std::bind is not really "perfect forwarding": it
> doesn't forward value category, and along with const-ness it also
> forwards volatile-ness (before C++20).
>
> The old implementation suffers from the same problem that other
> SFINAE-friendly call wrappers have which is solved by using deducing
> this (see p5.5 of the deducing this paper P0847R7).
>
>         PR libstdc++/80564
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/functional (__cv_like): New.
>         (_Bind::_Res_type): Don't define when not needed.
>         (_Bind::__dependent): Likewise.
>         (_Bind::_Res_type_cv): Likewise.
>         (_Bind::operator()) [_GLIBCXX_EXPLICIT_THIS_PARAMETER]:
>         Define as two instead of four overloads using deducing
>         this.
>         * testsuite/20_util/bind/cv_quals_2.cc: Ignore SFINAE
>         diagnostics inside headers.
>         * testsuite/20_util/bind/ref_neg.cc: Likewise.
>         * testsuite/20_util/bind/cv_quals_4.cc: New test.
> ---
>  libstdc++-v3/include/std/functional           | 82 +++++++++++++++++++
>  .../testsuite/20_util/bind/cv_quals_2.cc      |  3 +
>  .../testsuite/20_util/bind/cv_quals_4.cc      | 50 +++++++++++
>  .../testsuite/20_util/bind/ref_neg.cc         |  1 +
>  4 files changed, 136 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
>
> diff --git a/libstdc++-v3/include/std/functional
> b/libstdc++-v3/include/std/functional
> index 1928a27d3fd6..06ff32752d4a 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -495,6 +495,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  # define _GLIBCXX_DEPR_BIND
>  #endif
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +  // Return a _Up that has the same cv-quals as _Tp.
> +  template<typename _Tp, typename _Up> // _Up should be cv-unqualified
> +    struct __cv_like
> +    { using type = _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<const _Tp, _Up>
> +    { using type = const _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<volatile _Tp, _Up>
> +    { using type = volatile _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    struct __cv_like<const volatile _Tp, _Up>
> +    { using type = const volatile _Up; };
> +
> +  template<typename _Tp, typename _Up>
> +    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
> +#endif
> +
>    /// Type of the function object returned from bind().
>    template<typename _Signature>
>      class _Bind;
> @@ -564,6 +586,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         using _Res_type_impl
>           = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
>
> +#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
>        template<typename _CallArgs>
>         using _Res_type = _Res_type_impl<_Functor, _CallArgs,
> _Bound_args...>;
>
> @@ -576,6 +599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           typename __cv_quals<__dependent<_CallArgs>>::type,
>           _CallArgs,
>           typename __cv_quals<_Bound_args>::type...>;
> +#endif
>
>       public:
>        template<typename... _Args>
> @@ -593,6 +617,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _Bind(const _Bind&) = default;
>        _Bind(_Bind&&) = default;
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +# pragma GCC diagnostic push
> +# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
> +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
> +      template<typename... _Args,
> +              typename _Self,
> +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> +              __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
> +              typename _Result
> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
> +                                 tuple<_Args...>,
> +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> +       _GLIBCXX20_CONSTEXPR
> +       _Result
> +       operator()(this _Self&& __self, _Args&&... __args)
> +       {
> +         using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
> +         if constexpr (is_const<_Self_nonref>::value)
> +           return _Bind_ref(__self)
> +             .template __call_c<_Result>(std::forward_as_tuple
> +                                         (std::forward<_Args>(__args)...),
> +                                         _Bound_indexes());
> +         else
> +           return _Bind_ref(__self)
> +             .template __call<_Result>(std::forward_as_tuple
> +                                       (std::forward<_Args>(__args)...),
> +                                       _Bound_indexes());
> +       }
> +
> +# if defined(_GLIBCXX_VOLATILE_BIND)
> +      template<typename... _Args,
> +              typename _Self,
> +              typename _Self_nonref = typename
> remove_reference<_Self>::type,
> +              __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
> +              typename _Result
> +                = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
> +                                 tuple<_Args...>,
> +                                 __cv_like_t<_Self_nonref,
> _Bound_args>...>>
> +       _GLIBCXX_DEPR_BIND
> +       _Result
> +       operator()(this _Self&& __self, _Args&&... __args)
> +       {
> +         using _Bind_ref = __cv_like_t<_Self_nonref, _Bind>&;
> +         if constexpr (is_const<_Self_nonref>::value)
> +           return _Bind_ref(__self)
> +             .template __call_c_v<_Result>(std::forward_as_tuple
> +
>  (std::forward<_Args>(__args)...),
> +                                           _Bound_indexes());
> +         else
> +           return _Bind_ref(__self)
> +             .template __call_v<_Result>(std::forward_as_tuple
> +                                         (std::forward<_Args>(__args)...),
> +                                         _Bound_indexes());
> +       }
> +# endif
> +# pragma GCC diagnostic pop
> +#else
>        // Call unqualified
>        template<typename... _Args,
>                typename _Result = _Res_type<tuple<_Args...>>>
> @@ -642,6 +723,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               _Bound_indexes());
>         }
>  #endif // volatile
> +#endif
>      };
>
>    /// Type of the function object returned from bind<R>().
> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> index e4c348f7a3ca..6d37cc43fd3a 100644
> --- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
> @@ -44,6 +44,9 @@ void test01()
>                        // { dg-error "no match" "" { target c++20 } 43 }
>  }
>
> +// Ignore the reasons for deduction/substitution failure in the headers.
> +// { dg-prune-output "no type named 'type' in 'struct
> std::enable_if<false" }
> +
>  int main()
>  {
>    test01();
> diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> new file mode 100644
> index 000000000000..f6e1a1ec66da
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
> @@ -0,0 +1,50 @@
> +// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
> +// { dg-do compile { target c++14 } }
> +
> +#include <functional>
> +
> +struct A
> +{
> +  template<typename T>
> +  auto operator()(T&)
> +  { }
> +
> +  template<typename T>
> +  auto operator()(T&) const
> +  { T::fail; }
> +};
> +
> +void
> +test01()
> +{
> +  A a;
> +  std::bind(a, 0)(); // doesn't consider the const overload
> +  std::bind<void>(a, 0)();
> +}
> +
> +void
> +test02()
> +{
> +  auto f = [] (auto& x) { x = 1; };
> +  int i;
> +  std::bind(f, i)(); // doesn't try const-invoking the lambda
> +  std::bind<void>(f, i)();
> +}
> +
> +#if __cpp_variadic_using
> +template<typename... Ts>
> +struct overloaded : private Ts...
> +{
> +  overloaded(Ts... ts) : Ts(ts)... { }
> +  using Ts::operator()...;
> +};
> +
> +void
> +test03()
> +{
> +  A a;
> +  auto f = std::bind(a, 0);
> +  overloaded<decltype(f)> g(f);
> +  g();
> +}
> +#endif
> diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> index dd47c437d426..46cc4bb330e2 100644
> --- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
> @@ -51,6 +51,7 @@ void test02()
>  // Ignore the reasons for deduction/substitution failure in the headers.
>  // Arrange for the match to work on installed trees as well as build
> trees.
>  // { dg-prune-output "no type named 'type' in 'struct
> std::__invoke_result" }
> +// { dg-prune-output "no type named 'type' in 'struct
> std::enable_if<false" }
>
>  int main()
>  {
> --
> 2.52.0.154.gf0ef5b6d9b
>
  

Patch

diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 1928a27d3fd6..9348356f03ce 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -495,6 +495,28 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 # define _GLIBCXX_DEPR_BIND
 #endif
 
+#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
+  // Return a _Up that has the same cv-quals as _Tp.
+  template<typename _Tp, typename _Up>
+    struct __cv_like
+    { using type = _Up; };
+
+  template<typename _Tp, typename _Up>
+    struct __cv_like<const _Tp, _Up>
+    { using type = const _Up; };
+
+  template<typename _Tp, typename _Up>
+    struct __cv_like<volatile _Tp, _Up>
+    { using type = volatile _Up; };
+
+  template<typename _Tp, typename _Up>
+    struct __cv_like<const volatile _Tp, _Up>
+    { using type = const volatile _Up; };
+
+  template<typename _Tp, typename _Up>
+    using __cv_like_t = typename __cv_like<_Tp, _Up>::type;
+#endif
+
   /// Type of the function object returned from bind().
   template<typename _Signature>
     class _Bind;
@@ -564,6 +586,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	using _Res_type_impl
 	  = __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
 
+#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
       template<typename _CallArgs>
 	using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
 
@@ -576,6 +599,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  typename __cv_quals<__dependent<_CallArgs>>::type,
 	  _CallArgs,
 	  typename __cv_quals<_Bound_args>::type...>;
+#endif
 
      public:
       template<typename... _Args>
@@ -593,6 +617,62 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Bind(const _Bind&) = default;
       _Bind(_Bind&&) = default;
 
+#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
+      // Call unqualified
+      template<typename... _Args,
+	       typename _Self,
+	       typename _Self_nonref = typename remove_reference<_Self>::type,
+	       __enable_if_t<!is_volatile<_Self_nonref>::value, int> = 0,
+	       typename _Result
+		 = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
+				  tuple<_Args...>,
+				  __cv_like_t<_Self_nonref, _Bound_args>...>>
+	_GLIBCXX20_CONSTEXPR
+	_Result
+	operator()(this _Self&& __self, _Args&&... __args)
+	{
+	  if constexpr (is_const<_Self_nonref>::value)
+	    return static_cast<const _Bind&>(__self)
+	      .template __call_c<_Result>(std::forward_as_tuple
+					  (std::forward<_Args>(__args)...),
+					  _Bound_indexes());
+	  else
+	    return static_cast<_Bind&>(__self)
+	      .template __call<_Result>(std::forward_as_tuple
+					(std::forward<_Args>(__args)...),
+					_Bound_indexes());
+	}
+
+# if defined(_GLIBCXX_VOLATILE_BIND)
+      template<typename... _Args,
+	       typename _Self,
+	       typename _Self_nonref = typename remove_reference<_Self>::type,
+	       __enable_if_t<is_volatile<_Self_nonref>::value, int> = 0,
+	       typename _Result
+		 = _Res_type_impl<__cv_like_t<_Self_nonref, _Functor>,
+				  tuple<_Args...>,
+				  __cv_like_t<_Self_nonref, _Bound_args>...>>
+	_GLIBCXX_DEPR_BIND
+	_Result
+	operator()(this _Self&& __self, _Args&&... __args)
+	{
+	  if constexpr (is_const<_Self_nonref>::value)
+	    return static_cast<const volatile _Bind&>(__self)
+	      .template __call_c_v<_Result>(std::forward_as_tuple
+					    (std::forward<_Args>(__args)...),
+					    _Bound_indexes());
+	  else
+	    return static_cast<volatile _Bind&>(__self)
+	      .template __call_v<_Result>(std::forward_as_tuple
+					  (std::forward<_Args>(__args)...),
+					  _Bound_indexes());
+	}
+# endif
+# pragma GCC diagnostic pop
+#else
       // Call unqualified
       template<typename... _Args,
 	       typename _Result = _Res_type<tuple<_Args...>>>
@@ -642,6 +722,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      _Bound_indexes());
 	}
 #endif // volatile
+#endif
     };
 
   /// Type of the function object returned from bind<R>().
diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
index e4c348f7a3ca..6d37cc43fd3a 100644
--- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc
@@ -44,6 +44,9 @@  void test01()
 		       // { dg-error "no match" "" { target c++20 } 43 }
 }
 
+// Ignore the reasons for deduction/substitution failure in the headers.
+// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
+
 int main()
 {
   test01();
diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
new file mode 100644
index 000000000000..365a6ff4e00f
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_4.cc
@@ -0,0 +1,39 @@ 
+// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda
+// { dg-do compile { target c++14 } }
+
+#include <functional>
+
+struct A
+{
+  template<class T>
+  auto operator()(T&)
+  { }
+
+  template<class T>
+  auto operator()(T&) const
+  { T::fail; }
+};
+
+void
+test01()
+{
+  A a;
+  std::bind(a, 0)(); // doesn't consider the const overload
+  std::bind<void>(a, 0)();
+}
+
+void
+test02()
+{
+  auto f = [] (auto& x) { x = 1; };
+  int i;
+  std::bind(f, i)(); // doesn't try const-invoking the lambda
+  std::bind<void>(f, i)();
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
index dd47c437d426..46cc4bb330e2 100644
--- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc
@@ -51,6 +51,7 @@  void test02()
 // Ignore the reasons for deduction/substitution failure in the headers.
 // Arrange for the match to work on installed trees as well as build trees.
 // { dg-prune-output "no type named 'type' in 'struct std::__invoke_result" }
+// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
 
 int main()
 {