| Message ID | 20251205145558.1952982-1-ppalka@redhat.com |
|---|---|
| State | New |
| Headers |
Return-Path: <gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3409A4C31864 for <patchwork@sourceware.org>; Fri, 5 Dec 2025 14:57:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3409A4C31864 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=IuEXdUfq X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 864614C31864 for <gcc-patches@gcc.gnu.org>; Fri, 5 Dec 2025 14:56:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 864614C31864 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 864614C31864 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764946563; cv=none; b=Wvta56EuQj8U14EGBGtuBNponUL03ZFXqrjh23mUhmdoVvPOhD8zxUh5b3r0kLZpsnKQ0l88sbsRRtRCADPpF1jPbmvUvNFeKirwLwxC4ShUDP5TzHHfhewQVeIj6FcJawq/y8QGbHN1s922DnaCH6LIzgoQ97Ihe68T6FgyTJE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764946563; c=relaxed/simple; bh=OlMjzIRVrgwP53HflQprIZ3N1059GWPZZTk5CjBNNPU=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=u/6mRSojEfaHxTqWXmiH+ef6NISMKcAjEc1d7lCfQicGEm+Qfocum7lNXrao2y/hA3CoIaXYXNzeoEQJW4TcJewZlTDQUQuy1DQ1bN6CCMCBIN0A7CbCJz+vdz/2mGlmxtvEzJHQ549hnKEa4EuTRBozSlKJF65y9mCHqUDYZig= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 864614C31864 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1764946563; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=RFXobZLMoZ2/UVwF4LOsMaYnP/rBKL0zWFXY+A/QVCc=; b=IuEXdUfq7sbq0fegzO5dZNX6YPWp9yOFJ7hvzNIS9e2tmjOhKuJ8r7L7xz9kXFLpRuII6i GQKxYlJAGM4l3tnTHAQr0agpjQj6epe6e/pEMsS7ObT11g8SLxW4zI7McuwUd14dVQ3V3X BJEYMPie5ab0XD2jtvVObrS/XrakoyY= Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-447-qeVyvd7IPCeJgkTOe2_7uw-1; Fri, 05 Dec 2025 09:56:02 -0500 X-MC-Unique: qeVyvd7IPCeJgkTOe2_7uw-1 X-Mimecast-MFC-AGG-ID: qeVyvd7IPCeJgkTOe2_7uw_1764946561 Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-4edaa289e0dso7478951cf.3 for <gcc-patches@gcc.gnu.org>; Fri, 05 Dec 2025 06:56:02 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764946560; x=1765551360; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=RFXobZLMoZ2/UVwF4LOsMaYnP/rBKL0zWFXY+A/QVCc=; b=H6GeU4anDBamhUR5b15ZdzyRpm3ngL/tOT5ivjzwyjl4I6FHoXCvYrT7+jp09VrEX9 j8j+79wUBUNCfGf3xx7IzvXLlJOmji4H9GMMGzIIMvgPJAk6OGTlfoxJ+dznDvTVhRzs 6qJrOfVtRvOA/nP2pdcvb2TGWIMRuBEl5YW+FIp3+wX7R8iPNlwDxbkMdWBZ+zhxI9dh VHeSf+zV1D+E0hRNYw08nLhox/enrimeugTFI+/Jh52/5Np3FmHOlnBQRUgTc8bqBz/n ZcCWWwt39iI2XkfrShw27ocjLS+QAqMPlXLYjRAl1Pm4ubM9yOVlcPpj8GbMWm5sOEKL ClUg== X-Gm-Message-State: AOJu0YzFZAd14sYuxhz1NsQJ5nR7xmlRAUO4lgL6XApmKE0VjawY5p7j HGMKoZAklafKoC2w6VfxijAxe8iAv3oghiF13TWVFhZQ4NS48PPv6ITHFShRds0MfCORO5Mj9C3 ffiF2YYKhaZcRbRkrH0BzKUk5M4Heq7sxcW9ChdPIkmZFqXBQ43bcbcI13eUzQtw7op8gKFXvqb n0A0yj450bHjRJH0eZcmVicIV6anTBN5XKZ2srU/ji X-Gm-Gg: ASbGnctADDG4pbLlN6KtJt+ALAaXU/j9Qrnr6oD37DBjNtpBl/R0q+Xgv83gzxCjXEL fGuIZYVtC0/f+9ErTU+gYfsLunaOZ8tMVwCsK0LZvPGDoGp4Qy9Q6q3JlChYA4dyASxRVEjHfay i1AADZO83qkSAseTn0HoA23QMUCB50ywAy2Vo5TfMWYMPKyiaIzjjbOQEORa2aY2JwGuGxqcmeZ J5GoW0u1VXbtB73AjJysSHJuWmJClMbyOx7WxBQ2lbN91uj8EUTYTv/jnCWdG8GJYcgVK6Y71Y0 +FISHAGbe2MvcwqwMbxtAY9ipDK05PpoOj0gh47LiSoNn8VuEftrcH2dluwSO7QdV0BCBD4HzQw = X-Received: by 2002:ac8:5793:0:b0:4ed:a574:64cb with SMTP id d75a77b69052e-4f01b0c0edfmr99448621cf.3.1764946560517; Fri, 05 Dec 2025 06:56:00 -0800 (PST) X-Google-Smtp-Source: AGHT+IEUUjsLND1Q6tzh7xufFjIaihfkAnyf+hgQ4GeInMftkzlI4lOFCW4j+ArbigV0iMvI//howA== X-Received: by 2002:ac8:5793:0:b0:4ed:a574:64cb with SMTP id d75a77b69052e-4f01b0c0edfmr99448361cf.3.1764946559993; Fri, 05 Dec 2025 06:55:59 -0800 (PST) Received: from idea ([2600:4040:aa68:6000:9e8e:99ff:fed1:71f]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4f0276b0351sm27429671cf.8.2025.12.05.06.55.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 05 Dec 2025 06:55:59 -0800 (PST) From: Patrick Palka <ppalka@redhat.com> To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org, Patrick Palka <ppalka@redhat.com> Subject: [PATCH] libstdc++: Use deducing this in std::bind when available [PR80564] Date: Fri, 5 Dec 2025 09:55:58 -0500 Message-ID: <20251205145558.1952982-1-ppalka@redhat.com> X-Mailer: git-send-email 2.52.0.154.gf0ef5b6d9b MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: ZKZtZ7q4oKNsEE2T4YtujZ6Vgdu212fdYEyyQXnasDU_1764946561 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-13.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_PASS, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org |
| 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
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 > >
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 >> >>
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() {
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 >
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 > > >
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 >
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() {