On Mon, Oct 13, 2025 at 5:16 PM 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.
> ---
>
I am a bit uncomfortable that we no longer have test coverage for this part,
but I think this is step in a better direction, so LGTM.

>  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
> +      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