mirror of git://gcc.gnu.org/git/gcc.git
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 <tkaminsk@redhat.com> Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
This commit is contained in:
parent
a1d895cd7a
commit
83aab2f736
|
|
@ -496,6 +496,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
# define _GLIBCXX_DEPR_BIND
|
# define _GLIBCXX_DEPR_BIND
|
||||||
#endif
|
#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().
|
/// Type of the function object returned from bind().
|
||||||
template<typename _Signature>
|
template<typename _Signature>
|
||||||
class _Bind;
|
class _Bind;
|
||||||
|
|
@ -565,6 +587,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
using _Res_type_impl
|
using _Res_type_impl
|
||||||
= __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
|
= __invoke_result_t<_Fn&, _Mu_type<_BArgs, _CallArgs>&&...>;
|
||||||
|
|
||||||
|
#if !_GLIBCXX_EXPLICIT_THIS_PARAMETER
|
||||||
template<typename _CallArgs>
|
template<typename _CallArgs>
|
||||||
using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
|
using _Res_type = _Res_type_impl<_Functor, _CallArgs, _Bound_args...>;
|
||||||
|
|
||||||
|
|
@ -577,6 +600,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
typename __cv_quals<__dependent<_CallArgs>>::type,
|
typename __cv_quals<__dependent<_CallArgs>>::type,
|
||||||
_CallArgs,
|
_CallArgs,
|
||||||
typename __cv_quals<_Bound_args>::type...>;
|
typename __cv_quals<_Bound_args>::type...>;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename... _Args>
|
template<typename... _Args>
|
||||||
|
|
@ -594,6 +618,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
_Bind(const _Bind&) = default;
|
_Bind(const _Bind&) = default;
|
||||||
_Bind(_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
|
// Call unqualified
|
||||||
template<typename... _Args,
|
template<typename... _Args,
|
||||||
typename _Result = _Res_type<tuple<_Args...>>>
|
typename _Result = _Res_type<tuple<_Args...>>>
|
||||||
|
|
@ -643,6 +724,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
_Bound_indexes());
|
_Bound_indexes());
|
||||||
}
|
}
|
||||||
#endif // volatile
|
#endif // volatile
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Type of the function object returned from bind<R>().
|
/// Type of the function object returned from bind<R>().
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -44,6 +44,9 @@ void test01()
|
||||||
// { dg-error "no match" "" { target c++20 } 43 }
|
// { 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()
|
int main()
|
||||||
{
|
{
|
||||||
test01();
|
test01();
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ void test02()
|
||||||
// Ignore the reasons for deduction/substitution failure in the headers.
|
// Ignore the reasons for deduction/substitution failure in the headers.
|
||||||
// Arrange for the match to work on installed trees as well as build trees.
|
// 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::__invoke_result" }
|
||||||
|
// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" }
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue