While updating our C++17 features to add the new std::invoke_result trait I got annoyed with the repetition in std::_Not_fn, as it makes it hard to check it's defined correctly. Specifically, we need to repeat the cv-qualifier and ref-qualifiers in multiple places in each overload.
This introduces a macro that is expanded with one of "&" or "const &" or "&&" or "const &&" and so ensures it's used consistently. This only affects C++17 std::not_fn and std::experimental::not_fn so I'm committing it now. * include/std/functional (_Not_fn): Define macro to simplify repetitive function definitions. Tested powerpc64le-linux, committed to trunk.
commit 0eb6e44bcabb995d29b5569f27f221dd92027c00 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Mar 9 20:15:55 2017 +0000 Define macro to simplify std::_Not_fn definition * include/std/functional (_Not_fn): Define macro to simplify repetitive function definitions. diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index ea36dd0..366a7fb 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -902,15 +902,12 @@ _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type) template<typename _Fn> class _Not_fn { - template<typename _Tp> - using __is_nothrow_negatable - = __bool_constant<noexcept(!std::declval<_Tp>())>; - template<typename _Fn2, typename... _Args> - using __noexcept_cond = __and_< - __is_nothrow_callable<_Fn2(_Args&&...)>, - __is_nothrow_negatable<result_of_t<_Fn2(_Args&&...)>> - >; + using __inv_res_t = result_of_t<_Fn2(_Args&&...)>; + + template<typename _Tp> + static decltype(!std::declval<_Tp>()) + _S_not() noexcept(noexcept(!std::declval<_Tp>())); public: template<typename _Fn2> @@ -921,39 +918,23 @@ _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type) _Not_fn(_Not_fn&& __fn) = default; ~_Not_fn() = default; - template<typename... _Args> - auto - operator()(_Args&&... __args) & - noexcept(__noexcept_cond<_Fn&, _Args&&...>::value) - -> decltype(!std::declval<result_of_t<_Fn&(_Args&&...)>>()) - { return !std::__invoke(_M_fn, std::forward<_Args>(__args)...); } - - template<typename... _Args> - auto - operator()(_Args&&... __args) const & - noexcept(__noexcept_cond<const _Fn&, _Args&&...>::value) - -> decltype(!std::declval<result_of_t<const _Fn&(_Args&&...)>>()) - { return !std::__invoke(_M_fn, std::forward<_Args>(__args)...); } - - template<typename... _Args> - auto - operator()(_Args&&... __args) && - noexcept(__noexcept_cond<_Fn&&, _Args&&...>::value) - -> decltype(!std::declval<result_of_t<_Fn&&(_Args&&...)>>()) - { - return !std::__invoke(std::move(_M_fn), - std::forward<_Args>(__args)...); - } - - template<typename... _Args> - auto - operator()(_Args&&... __args) const && - noexcept(__noexcept_cond<const _Fn&&, _Args&&...>::value) - -> decltype(!std::declval<result_of_t<const _Fn&&(_Args&&...)>>()) - { - return !std::__invoke(std::move(_M_fn), - std::forward<_Args>(__args)...); + // 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. +#define _GLIBCXX_NOT_FN_CALL_OP( _QUALS ) \ + template<typename... _Args> \ + decltype(_S_not<__inv_res_t<_Fn _QUALS, _Args...>>()) \ + operator()(_Args&&... __args) _QUALS \ + noexcept(noexcept(_S_not<__inv_res_t<_Fn _QUALS, _Args...>>())) \ + { \ + return !std::__invoke(std::forward< _Fn _QUALS >(_M_fn), \ + std::forward<_Args>(__args)...); \ } + _GLIBCXX_NOT_FN_CALL_OP( & ) + _GLIBCXX_NOT_FN_CALL_OP( const & ) + _GLIBCXX_NOT_FN_CALL_OP( && ) + _GLIBCXX_NOT_FN_CALL_OP( const && ) +#undef _GLIBCXX_NOT_FN_CALL private: _Fn _M_fn;