libstdc++: Fix std::not_fn perfect forwarding [PR111327]

The previous patch fixed perfect forwarding in std::bind_front.
This patch fixes the same issue in std::not_fn.

	PR libstdc++/111327

libstdc++-v3/ChangeLog:

	* include/std/functional (_GLIBCXX_NOT_FN_CALL_OP): Also define
	a deleted fallback operator() overload.  Constrain both the
	enabled and deleted overloads accordingly.
	* testsuite/20_util/function_objects/not_fn/111327.cc: New test.
This commit is contained in:
Patrick Palka 2023-09-12 11:26:50 -04:00
parent 4289f6ceef
commit 52f65d17c8
2 changed files with 37 additions and 2 deletions

View file

@ -1061,7 +1061,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// forwarding _M_fn and the function arguments with the same qualifiers,
// and deducing the return type and exception-specification.
#define _GLIBCXX_NOT_FN_CALL_OP( _QUALS ) \
template<typename... _Args> \
template<typename... _Args, \
typename = enable_if_t<__is_invocable<_Fn _QUALS, _Args...>::value>> \
_GLIBCXX20_CONSTEXPR \
decltype(_S_not<__inv_res_t<_Fn _QUALS, _Args...>>()) \
operator()(_Args&&... __args) _QUALS \
@ -1070,7 +1071,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ \
return !std::__invoke(std::forward< _Fn _QUALS >(_M_fn), \
std::forward<_Args>(__args)...); \
}
} \
\
template<typename... _Args, \
typename = enable_if_t<!__is_invocable<_Fn _QUALS, _Args...>::value>> \
void operator()(_Args&&... __args) _QUALS = delete;
_GLIBCXX_NOT_FN_CALL_OP( & )
_GLIBCXX_NOT_FN_CALL_OP( const & )
_GLIBCXX_NOT_FN_CALL_OP( && )

View file

@ -0,0 +1,29 @@
// PR libstdc++/111327 - std::bind_front (and std::not_fn) doesn't always
// perfectly forward according to value category of the call wrapper object
// { dg-do compile { target c++17 } }
#include <functional>
#include <utility>
struct F {
void operator()(...) & = delete;
bool operator()(...) const &;
};
struct G {
void operator()(...) && = delete;
bool operator()(...) const &&;
};
int main() {
auto f = std::not_fn(F{});
f(); // { dg-error "deleted" }
std::move(f)();
std::as_const(f)();
std::move(std::as_const(f))();
auto g = std::not_fn(G{});
g(); // { dg-error "deleted" }
std::move(g)(); // { dg-error "deleted" }
std::move(std::as_const(g))();
}