https://gcc.gnu.org/g:83aab2f73661ac8aa1643d58d2fbfed87d5c1601
commit r16-5930-g83aab2f73661ac8aa1643d58d2fbfed87d5c1601 Author: Patrick Palka <[email protected]> Date: Fri Dec 5 21:09:34 2025 -0500 libstdc++: Use deducing this in std::bind when available [PR80564] Implement the forwarding performed by std::bind via deducing this when available, instead of needing 4 operator() overloads. Using deducing this here is more complicated than in other standard call wrappers because std::bind is not really "perfect forwarding": it doesn't consider value category, and along with const-ness it also forwards volatile-ness (until C++20). The old implementation suffers from the same problem that other pre-C++23 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/80564.cc: New test. Reviewed-by: Tomasz KamiĆski <[email protected]> Reviewed-by: Jonathan Wakely <[email protected]> Diff: --- libstdc++-v3/include/std/functional | 82 +++++++++++++++++++++++ libstdc++-v3/testsuite/20_util/bind/80564.cc | 50 ++++++++++++++ libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc | 3 + libstdc++-v3/testsuite/20_util/bind/ref_neg.cc | 1 + 4 files changed, 136 insertions(+) diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 74aa5fa3e9a1..570e9e9c8fbc 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -496,6 +496,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; @@ -565,6 +587,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...>; @@ -577,6 +600,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename __cv_quals<__dependent<_CallArgs>>::type, _CallArgs, typename __cv_quals<_Bound_args>::type...>; +#endif public: template<typename... _Args> @@ -594,6 +618,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...>>> @@ -643,6 +724,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/80564.cc b/libstdc++-v3/testsuite/20_util/bind/80564.cc new file mode 100644 index 000000000000..f6e1a1ec66da --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bind/80564.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/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/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() {
