On Mon, 13 Oct 2025 at 16:15, Patrick Palka <[email protected]> wrote:
>
> Implement the perfect forwarding required by std::not_fn using deducing
> this when available, instead of needing 8 operator() overloads.  This
> also fixes Jiang An's test from this PR which would be messy to fix in
> the old implementation.
>
>         PR libstdc++/111327
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/functional (_Not_fn::operator())
>         [_GLIBCXX_EXPLICIT_THIS_PARAMETER]: Define as a single
>         overload using deducing this.
>         * testsuite/20_util/function_objects/not_fn/111327.cc: Extend test.
> ---
>  libstdc++-v3/include/std/functional           | 16 ++++++++++++++++
>  .../20_util/function_objects/not_fn/111327.cc | 19 ++++++++++++++++---
>  2 files changed, 32 insertions(+), 3 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/functional 
> b/libstdc++-v3/include/std/functional
> index 6f7f2f627a2a..6122dda65e9b 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -1091,6 +1091,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _Not_fn(_Not_fn&& __fn) = default;
>        ~_Not_fn() = default;
>
> +#if _GLIBCXX_EXPLICIT_THIS_PARAMETER
> +# pragma GCC diagnostic push
> +# pragma GCC diagnostic ignored "-Wc++23-extensions" // deducing this
> +      template<typename _Self, typename... _Args>
> +      _GLIBCXX20_CONSTEXPR

As an aside I think we could make all the member functions of _Not_fn
unconditionally constexpr.
As long as the std::not_fn function isn't constexpr until C++20 you
wouldn't actually be able to create a _Not_fn in constant expressions,
so it would be harmless for the members to be constexpr.
That would allow us to use 'constexpr' instead of the ugly macro.

But that would be a separate change; this is OK for trunk, thanks.



> +      decltype(_S_not<__inv_res_t<__like_t<_Self, _Fn>, _Args...>>())
> +      operator()(this _Self&& __self, _Args&&... __args)
> +      noexcept(__is_nothrow_invocable<__like_t<_Self, _Fn>, _Args...>::value
> +              && noexcept(_S_not<__inv_res_t<__like_t<_Self, _Fn>, 
> _Args...>>()))
> +      {
> +       return !std::__invoke(__like_t<_Self, _Not_fn>(__self)._M_fn,
> +                             std::forward<_Args>(__args)...);
> +      }
> +# pragma GCC diagnostic pop
> +#else
>        // Macro to define operator() with given cv-qualifiers ref-qualifiers,
>        // forwarding _M_fn and the function arguments with the same 
> qualifiers,
>        // and deducing the return type and exception-specification.
> @@ -1116,6 +1131,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        _GLIBCXX_NOT_FN_CALL_OP( && )
>        _GLIBCXX_NOT_FN_CALL_OP( const && )
>  #undef _GLIBCXX_NOT_FN_CALL_OP
> +#endif
>
>      private:
>        _Fn _M_fn;
> diff --git a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc 
> b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc
> index 725a84262716..be58b0e28d1a 100644
> --- a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc
> +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc
> @@ -15,15 +15,28 @@ struct G {
>    bool operator()(...) const &&;
>  };
>
> +struct Weird {
> +  void operator()();
> +  bool operator()() const { return true; }
> +};
> +
>  int main() {
>    auto f = std::not_fn(F{});
> -  f(); // { dg-error "deleted" }
> +  f(); // { dg-error "no match" }
>    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" }
> +  g(); // { dg-error "no match" }
> +  std::move(g)(); // { dg-error "no match" }
>    std::move(std::as_const(g))();
> +
> +  auto h = std::not_fn(Weird{});
> +  h(); // { dg-error "no match" }
>  }
> +
> +// { dg-error "no type named 'type' in 'struct std::__invoke_result<" "" { 
> target *-*-* } 0 }
> +// { dg-error "no matching function for call to 'std::_Not_fn<Weird>" "" { 
> target *-*-* } 0 }
> +// { dg-error "could not convert 'std::declval<void>\\(\\)' from 'void' to 
> 'bool'" "" { target *-*-* } 0 }
> +// { dg-error "in argument to unary !" "" { target *-*-* } 0 }
> --
> 2.51.0.491.g4b71b29477
>

Reply via email to