В этом вопросе я рассматриваю реализацию libstdc ++ оболочки вызовов _Not_fn
.
Она определяет четыре перегрузки оператора вызова функции следующим образом:
#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
Легко видеть, что спецификация noexcept установлена как:
noexcept(noexcept(_S_not<__inv_res_t<_Fn _QUALS, _Args...>>()))
, где __inv_res_t
- шаблон псевдонима:
template<typename _Fn2, typename... _Args>
using __inv_res_t = typename __invoke_result<_Fn2, _Args...>::type;
и _S_not
является шаблоном статической функции-члена:
template<typename _Tp>
static decltype(!std::declval<_Tp>())
_S_not() noexcept(noexcept(!std::declval<_Tp>()));
Теперь, следуя логике, лежащей в основе спецификации noexcept, я заключаю, что:
- Оператор вызова функции концептуально имеет ту же спецификацию noexcept, что и
_S_not<__inv_res_t<_Fn _QUALS, _Args...>>
. _S_not<__inv_res_t<_Fn _QUALS, _Args...>>
помечается как noexcept, в зависимости от того, является ли отрицание, примененное к результату std::__invoke(...)
, исключением.
С моей точки зрения, эта спецификация noexcept не распространяется на случай, когда вызываемый объект, заключенный в not_fn
, может или не может сам броситься, будучи вызванным с определенным набором аргументов.передан в not_fn
вызов функцииоператор.Другими словами, не проверяется, может ли само std::__invoke(...)
внутри оператора вызова функции выдавать или нет .
Я что-то упускаю в этой реализации?
реализация от cppreference.com имеет немного более простую, кроме спецификации.Однако эта реализация не работает с последней версией g ++ из-за известной проблемы .